Created
January 20, 2013 21:39
-
-
Save gradha/4581948 to your computer and use it in GitHub Desktop.
The docstring for open_chunk contains a single ` which causes "nimepakoo.nim(153, 2) Error: '`' expected", but line 153 is in the getc proc, much later.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Convenience wrapper around raw `nimepak <nimepak.html>`_ module. | |
## | |
## The usual operation with these procs is to create a Tepak var and call | |
## init() on it to use it. You can call is_valid() at any time on a Tepak to | |
## check its validity. | |
import nimepak | |
export nimepak.F_BUF_SIZE | |
export nimepak.F_PACK_MAGIC | |
export nimepak.F_NOPACK_MAGIC | |
export nimepak.F_EXE_MAGIC | |
export nimepak.PACKFILE_FLAG_WRITE | |
export nimepak.PACKFILE_FLAG_PACK | |
export nimepak.PACKFILE_FLAG_CHUNK | |
export nimepak.PACKFILE_FLAG_EOF | |
export nimepak.PACKFILE_FLAG_ERROR | |
export nimepak.PACKFILE_FLAG_OLD_CRYPT | |
export nimepak.PACKFILE_FLAG_EXEDAT | |
export nimepak.F_READ | |
export nimepak.F_WRITE | |
export nimepak.F_READ_PACKED | |
export nimepak.F_WRITE_PACKED | |
export nimepak.F_WRITE_NOPACK | |
export nimepak.packfile_password | |
type | |
Tepak* = object | |
filename*: string | |
mode*: string | |
pak*: PPACKFILE | |
proc is_valid*(self: Tepak): bool {.inline.} = | |
result = self.pak != nil | |
proc init*(self: var Tepak; filename, mode: string): bool = | |
## Initializes a Tepak structure opening a file. | |
## | |
## Pass the filename and mode you want to open this file in, which may | |
## contain any of the following flags: | |
## | |
## `r` - open file for reading. | |
## | |
## `w` - open file for writing, overwriting any existing data. | |
## | |
## `p` - open file in packed mode. Data will be compressed as it is written | |
## to the file, and automatically uncompressed during read operations. Files | |
## created in this mode will produce garbage if they are read without this | |
## flag being set. | |
## | |
## `!` - open file for writing in normal, unpacked mode, but add the value | |
## F_NOPACK_MAGIC to the start of the file, so that it can later be opened in | |
## packed mode and Epak will automatically detect that the data does not | |
## need to be decompressed. | |
## | |
## Instead of these flags, one of the constants F_READ, F_WRITE, | |
## F_READ_PACKED, F_WRITE_PACKED or F_WRITE_NOPACK may be used as the mode | |
## parameter. | |
## | |
## Returns true if the initialisation worked and you can use the structure. | |
self.filename = filename | |
self.mode = mode | |
self.pak = pack_fopen(filename, mode) | |
result = self.is_valid() | |
proc new_epak*(filename, mode: string): Tepak = | |
## Convenience wrapper around init to create vars. | |
## | |
## Note that you should use is_valid() on the returned structure to check if | |
## it worked, or you could fail hard. | |
discard(result.init(filename, mode)) | |
proc close*(self: var Tepak): bool {.discardable.} = | |
## Closes the Tepak. | |
## | |
## After you have closed the stream, performing operations on it will yield | |
## errors in your application (e.g. crash it) or even block your OS. | |
## | |
## Returns zero on success. On error, returns false and stores the error code | |
## in `errno`. This function can fail only when writing to files: if the file | |
## was opened in read mode, it will always succeed. | |
if self.pak != nil: | |
let ret = pack_fclose(self.pak) | |
if ret != 0: | |
echo "Problems closing epak for ", self.filename | |
else: | |
self.pak = nil | |
result = self.pak == nil | |
proc seek*(self: var Tepak; offset: cint): int {.discardable.} = | |
## Seeks in the epak, returns amount of bytes seeked: | |
## | |
## Unlike the standard fseek() function, this only supports forward movements | |
## relative to the current position and in read-only streams, so don't use | |
## negative offsets. Note that seeking is very slow when reading compressed | |
## files, and so should be avoided unless you are sure that the file is not | |
## compressed. | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_fseek(self.pak, offset) | |
proc skip_chunks*(self: var Tepak; to_skip: cuint): bool {.discardable.} = | |
## Efficient proc to skip several chunks in your packfile. | |
## | |
## This is faster than opening and closing a subchunk as you find it on disk. | |
## The function reads the hidden chunk size data and uses that to call | |
## pack_fseek instead. Pass how many chunks you want to skip, usually one. | |
## | |
## Returns true if the specified number of chunks was skipped, false | |
## otherwise, storing the error code in `errno`. | |
assert self.pak != nil, "Unexpected bad epak" | |
if pack_skip_chunks(self.pak, to_skip) == 0: | |
result = true | |
proc open_chunk*(self: var Tepak; use_compression: bool): bool {.discardable.} = | |
## Opens a sub-chunk of a file. | |
## | |
## A chunk provides a logical view of part of a file, which can be compressed | |
## as an individual entity and will automatically insert and check length | |
## counts to prevent reading past the end of the chunk. If use_compression is | |
## true this will turn on compression for the sub-chunk. | |
## | |
## The data written to the chunk will be prefixed with two length | |
## counts (32-bit, a.k.a. big-endian). For uncompressed chunks these | |
## will both be set to the size of the data in the chunk. For compressed | |
## chunks (created by setting the `pack flag), the first length will | |
## be the raw size of the chunk, and the second will be the negative | |
## size of the uncompressed data. | |
## | |
## Chunks can be nested inside each other by making repeated calls | |
## to open_chunk(). When writing a file, the compression status | |
## is inherited from the parent file, so you only need to set the | |
## compression flag if the parent is not compressed but you want to pack the | |
## chunk data. If the parent file is already open in packed mode, | |
## setting the pack flag will result in data being compressed twice: | |
## once as it is written to the chunk, and again as the chunk passes | |
## it on to the parent file. | |
## | |
## Returns true if the operation succeeded. | |
assert self.pak != nil, "Unexpected bad epak" | |
self.pak = pack_fopen_chunk(self.pak, if use_compression: 1 else: 0) | |
result = self.is_valid() | |
proc close_chunk*(self: var Tepak): bool {.discardable.} = | |
## Closes a sub-chunk of a file, previously opened with open_chunk(). | |
## | |
## Returns false if there was some error. | |
assert self.pak != nil, "Unexpected bad epak" | |
self.pak = pack_fclose_chunk(self.pak) | |
result = self.is_valid() | |
proc getc*(self: var Tepak): cint = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_getc(self.pak) | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc putc*(self: var Tepak; value: cint) = | |
assert self.pak != nil, "Unexpected bad epak" | |
let ret = pack_putc(value, self.pak) | |
assert ret == value, "Didn't write input value" | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc eof*(self: var Tepak): bool = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = (pack_feof(self.pak) != 0) | |
proc igetw*(self: var Tepak): cint = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_igetw(self.pak) | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc igetl*(self: var Tepak): clong = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_igetl(self.pak) | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc iputw*(self: var Tepak; value: cint) = | |
assert self.pak != nil, "Unexpected bad epak" | |
let ret = pack_iputw(value, self.pak) | |
assert ret == value, "Didn't write input value" | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc iputl*(self: var Tepak; value: clong) = | |
assert self.pak != nil, "Unexpected bad epak" | |
let ret = pack_iputl(value, self.pak) | |
assert ret == value, "Didn't write input value" | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc mgetw*(self: var Tepak): cint = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_mgetw(self.pak) | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc mgetl*(self: var Tepak): clong = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_mgetl(self.pak) | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc mputw*(self: var Tepak; value: cint) = | |
assert self.pak != nil, "Unexpected bad epak" | |
let ret = pack_mputw(value, self.pak) | |
assert ret == value, "Didn't write input value" | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc mputl*(self: var Tepak; value: clong) = | |
assert self.pak != nil, "Unexpected bad epak" | |
let ret = pack_mputl(value, self.pak) | |
assert ret == value, "Didn't write input value" | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc read*(self: var Tepak; dest: pointer; length: clong): clong = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_fread(dest, length, self.pak) | |
assert result == length, "Didn't read expected char count" | |
proc read*(self: var Tepak; length: clong): string = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = newString(length) | |
let read_length = pack_fread(cstring(result), length, self.pak) | |
result.setLen(read_length) | |
assert read_length == length, "Didn't read expected char count" | |
proc write*(self: var Tepak; src: pointer; length: clong): | |
clong {.discardable.} = | |
assert self.pak != nil, "Unexpected bad epak" | |
result = pack_fwrite(src, length, self.pak) | |
assert result == length, "Didn't write all bytes?" | |
assert pack_ferror(self.pak) == 0, "Error pending" | |
proc write*(self: var Tepak; text: string; append_terminator = true): | |
clong {.discardable.} = | |
## Convenience wrapper, writes the string including null terminator by default | |
assert self.pak != nil, "Unexpected bad epak" | |
let text_len = if append_terminator: text.len + 1 else: text.len | |
result = pack_fwrite(cstring(text), clong(text_len), self.pak) | |
assert result == text_len, "Didn't write all bytes?" | |
assert pack_ferror(self.pak) == 0, "Error pending" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment