view: render map entities
[d2df-maped.git] / src / vfs.nim
blob9038043c2e69f05c07547b6e8885a19f9e77e5bd
1 import
2   std/os, std/streams, std/strutils, std/tables,
3   vfs/[common, dfwad, zip],
4   utils
6 export
7   streams, common, dfwad, zip
9 const
10   archiveExts* = [".zip", ".dfz", ".pk3", ".wad"]
12 var
13   archives: seq[FsArchive] = @[]
14   mapArchive*: FsArchive = nil
15   mapDeps*: seq[FsArchive] = @[]
17 proc openArchive*(path: string, write: bool, forceExisting: bool = false): FsArchive =
18   result = nil
19   let existing = fileExists(path)
20   let (dir, name, ext) = splitFile(path)
21   let arcName = name & ext
22   let mode = (if write: (if forceExisting: fmReadWriteExisting else: fmReadWrite) else: fmRead)
23   var stream = newFileStream(path, mode)
24   if stream != nil:
25     if stream.checkIfWad():
26       result = newFsWadArchive(arcName, path)
27     elif stream.checkIfZip():
28       result = newFsZipArchive(arcName, path)
29     else:
30       raise newException(FsFormatError, "Unknown archive format extension: " & ext)
31     result.stream = stream
32     result.writeable = write
33     result.existing = existing
34     result.open()
35   else:
36     raise newException(IOError, "Could not open " & path)
38 proc openArchive*(name: string, stream: Stream): FsArchive =
39   result = nil
40   if stream.checkIfWad():
41     result = newFsWadArchive(name, "")
42   elif stream.checkIfZip():
43     result = newFsZipArchive(name, "")
44   else:
45     return
46   result.stream = stream
47   result.writeable = false
48   result.existing = true
49   result.open()
51 proc unmount*(self: FsArchive) =
52   var idx = archives.find(self)
53   if idx >= 0:
54     archives.delete(idx)
56   if self == mapArchive:
57     mapArchive = nil
58   else:
59     idx = mapDeps.find(self)
60     if idx >= 0:
61       mapDeps.delete(idx)
63   self.close()
65 proc unmountAll*() =
66   while archives.len() > 0:
67     var ar = archives.pop()
68     ar.close()
69   mapArchive = nil
70   mapDeps.setLen(0)
72 proc unmountMapDeps*() =
73   while mapDeps.len() > 0:
74     var ar = mapDeps.pop()
75     ar.close()
76   mapDeps.setLen(0)
78 proc unmountMap*() =
79   if mapArchive != nil:
80     mapArchive.unmount()
82 proc findArchive*(arName: string): FsArchive =
83   if arName == "":
84     return mapArchive
85   for i, ar in archives:
86     if cmpIgnoreCase(ar.name, arName) == 0:
87       return ar
88   result = nil
90 proc mount*(path: string, write: bool, forceExisting: bool = false, isMapArchive: bool = false, isMapDep: bool = false): FsArchive {.discardable.} =
91   if path != "":
92     let (dir, name, ext) = splitFile(path)
93     var ar = findArchive(name & ext)
94     if ar != nil: return ar # don't mount it again
96   if isMapArchive:
97     unmountMap()
99   result = openArchive(path, write, forceExisting)
101   if isMapArchive:
102     mapArchive = result
104   if isMapDep:
105     mapDeps.add(result)
107   archives.add(result)
109 proc init*() =
110   addExitProc(vfs.unmountAll)
112 proc splitResPath*(path: string): tuple[archive, path: string] =
113   var toks = path.split(':')
114   if toks.len() < 2:
115     raise newException(ValueError, "Invalid resource path: " & path)
116   if toks[1][0] == '/':
117     result = (toks[0], toks[1][1 .. ^1].toUpperAscii())
118   else:
119     result = (toks[0], toks[1].toUpperAscii())
121 proc open*(path: string, write: bool): Stream =
122   let (arName, localPath) = path.splitResPath()
123   var ar = findArchive(arName)
124   if ar != nil:
125     if write:
126       return ar.writeFile(localPath)
127     else:
128       return ar.readFile(localPath)
129   elif arName != "":
130     # can't find archive in vfs; try to mount one
131     var ar: FsArchive
132     if fileExists(arName):
133       ar = vfs.mount(arName, write, not write, isMapDep = not write)
134     elif fileExists("wads/" & arName):
135       ar = vfs.mount("wads/" & arName, write, not write, isMapDep = not write)
136     else:
137       return nil
138     # archive found, get the res
139     if ar != nil:
140       if write:
141         return ar.writeFile(localPath)
142       else:
143         return ar.readFile(localPath)
144     result = nil
146 proc resExists*(path: string): bool =
147   let (arName, localPath) = path.splitResPath()
148   var ar = findArchive(arName)
149   if ar == nil:
150     result = false
151   else:
152     result = (ar.findFile(localPath) != nil)
154 iterator resources*(self: FsArchive, prefix: string = ""): FsFileEntry =
155   let upperPrefix = prefix.toUpperAscii()
156   for fname, f in self.files:
157     if fname.startsWith(upperPrefix):
158       yield f
160 iterator resources*(prefix: string): FsFileEntry =
161   let (arName, localPath) = prefix.splitResPath()
162   var ar = findArchive(arName)
163   if ar != nil:
164     for fname, f in ar.files:
165       if fname.startsWith(localPath):
166         yield f