2 std/streams, std/tables, std/os, std/strutils,
6 FsFileEntry* = ref object of RootObj
9 data*: string # if this is "". file is yet to be read
10 dataOfs*: int # if this is -1, file is yet to be written
13 uncompressedSize*: int # if this is -1, length is unknown
15 FsArchive* = ref object of RootObj
18 files*: Table[string, FsFileEntry]
24 FsStreamObj* = object of StringStreamObj
27 FsStream* = ref FsStreamObj
29 FsFormatError* = object of IOError
30 FsReadError* = object of IOError
31 FsWriteError* = object of IOError
33 method open*(self: FsArchive) {.base.} =
34 raiseBaseMethodCall("open")
36 method save*(self: FsArchive) {.base.} =
37 raiseBaseMethodCall("save")
39 method loadFile*(self: FsArchive, f: FsFileEntry) {.base.} =
40 raiseBaseMethodCall("loadFile")
42 method close*(self: FsArchive) {.base.} =
43 if self.writeable and self.changed:
44 # read the contents of all already existing files
45 for f in self.files.mvalues():
48 # if operating on a file, make a backup and truncate it
52 copyFile(self.path, self.path & ".bak")
55 self.stream = newFileStream(self.path, fmWrite)
60 if self.path != "" and self.stream != nil:
64 proc newFsFileEntry*(archive: FsArchive, path: string): FsFileEntry =
65 result = new(FsFileEntry)
66 result.parent = archive
68 result.uncompressedSize = -1
72 proc fsClose(s: Stream) =
74 var ss = StringStream(s)
76 f.entry.data = ss.data
77 f.entry.dataOfs = -1 # need rewrite
78 if f.entry.compressed:
79 f.entry.dataSize = -1 # need compression
81 f.entry.dataSize = ss.data.len()
82 f.entry.uncompressedSize = ss.data.len()
83 f.entry.parent.files[f.entry.path.toUpperAscii()] = f.entry
85 f.entry.data = "" # don't leave that shit cached
88 proc newFsStream*(f: FsFileEntry, write: bool): FsStream =
90 result = new(FsStream)
91 result.writing = write
93 # these are not really made to inherit from them
94 var ss = newStringStream(data)
96 result.closeImpl = fsClose # our own proc
97 result.atEndImpl = ss.atEndImpl
98 result.setPositionImpl = ss.setPositionImpl
99 result.getPositionImpl = ss.getPositionImpl
100 result.readDataStrImpl = ss.readDataStrImpl
101 result.readLineImpl = ss.readLineImpl
102 result.readDataImpl = ss.readDataImpl
103 result.peekDataImpl = ss.peekDataImpl
104 result.writeDataImpl = ss.writeDataImpl
105 result.flushImpl = ss.flushImpl
107 proc findFile*(self: FsArchive, localPath: string): FsFileEntry =
108 let upperPath = localPath.toUpperAscii()
109 if upperPath in self.files:
110 return self.files[upperPath]
113 proc loadFile*(self: FsArchive, localPath: string) =
114 var f = self.findFile(localPath)
116 raise newException(FsReadError, "file " & localPath & " does not exist in " & self.name)
119 proc writeFile*(self: FsArchive, localPath: string, compressed: bool = true): Stream =
120 if not self.writeable:
121 raise newException(FsWriteError, self.name & " is not writeable")
122 var f = self.findFile(localPath)
124 f = self.newFsFileEntry(localPath.toUpperAscii())
125 f.compressed = compressed
128 result = newFsStream(f, true)
130 proc readFile*(self: FsArchive, localPath: string): Stream =
131 var f = self.findFile(localPath)
135 result = newFsStream(f, false)