1225
|
1 type
|
|
2 ResourceBundlingType = enum
|
|
3 Dir # Directories
|
|
4 Zip # Zip files
|
|
5 Exe # Embeded in executable
|
|
6
|
|
7 const
|
|
8 thebundletype = parseEnum[ResourceBundlingType](PACKAGETYPE.toLowerAscii().capitalizeAscii())
|
|
9 ASCII_CHARSET = PrintableChars.toSeq.toRunes
|
|
10 DEFAULT_PACKAGE = "default"
|
|
11
|
|
12 # resource loading
|
|
13
|
|
14 func normalizeDir(dir: string): string =
|
|
15 result = dir
|
|
16 if result.startsWith("./"):
|
|
17 result = result[2 .. ^1]
|
|
18 if result.startsWith("/"):
|
|
19 result = result[1 .. ^1]
|
|
20 result = dir.replace('\\', '/')
|
|
21 if not result.endsWith("/") and result != "":
|
|
22 result = result & "/"
|
|
23
|
|
24 when thebundletype == Dir:
|
|
25
|
|
26 proc resourceRoot(): string =
|
|
27 getAppDir().absolutePath().joinPath(RESOURCEROOT)
|
|
28 proc packageRoot(package: string): string =
|
|
29 resourceRoot().joinPath(package)
|
|
30
|
|
31 proc loadResource_intern(path: string, package: string): Stream =
|
|
32 let realpath = package.packageRoot().joinPath(path)
|
|
33 if not realpath.fileExists():
|
|
34 raise newException(Exception, &"Resource {path} not found (checked {realpath})")
|
|
35 newFileStream(realpath, fmRead)
|
|
36
|
|
37 proc modList_intern(): seq[string] =
|
|
38 for kind, file in walkDir(resourceRoot(), relative = true):
|
|
39 if kind == pcDir:
|
|
40 result.add file
|
|
41
|
|
42 iterator walkResources_intern(dir: string, package = DEFAULT_PACKAGE): string =
|
|
43 for file in walkDirRec(package.packageRoot().joinPath(dir), relative = true):
|
|
44 yield file
|
|
45
|
|
46 iterator ls_intern(dir: string, package: string): tuple[kind: PathComponent, path: string] =
|
|
47 for i in walkDir(package.packageRoot().joinPath(dir), relative = true):
|
|
48 yield i
|
|
49
|
|
50 elif thebundletype == Zip:
|
|
51
|
|
52 import ./thirdparty/zippy/zippy/ziparchives
|
|
53
|
|
54 proc resourceRoot(): string =
|
|
55 absolutePath(getAppDir()).joinPath(RESOURCEROOT)
|
|
56 proc packageRoot(package: string): string =
|
|
57 resourceRoot().joinPath(package)
|
|
58
|
|
59 proc loadResource_intern(path: string, package: string): Stream =
|
|
60 let archive = openZipArchive(package.packageRoot() & ".zip")
|
|
61 try:
|
|
62 result = newStringStream(archive.extractFile(path))
|
|
63 except ZippyError:
|
|
64 raise newException(Exception, &"Resource {path} not found")
|
|
65 archive.close()
|
|
66
|
|
67 proc modList_intern(): seq[string] =
|
|
68 for kind, file in walkDir(resourceRoot(), relative = true):
|
|
69 if kind == pcFile and file.endsWith(".zip"):
|
|
70 result.add file[0 ..< ^4]
|
|
71
|
|
72 iterator walkResources_intern(dir: string, package = DEFAULT_PACKAGE): string =
|
|
73 let archive = openZipArchive(package.packageRoot() & ".zip")
|
|
74 let normDir = dir.normalizeDir()
|
|
75 for i in archive.walkFiles:
|
|
76 if i.startsWith(normDir):
|
|
77 yield i
|
|
78 archive.close()
|
|
79
|
|
80 iterator ls_intern(dir: string, package: string): tuple[kind: PathComponent, path: string] =
|
|
81 let archive = openZipArchive(package.packageRoot() & ".zip")
|
|
82 let normDir = dir.normalizeDir()
|
|
83 var yielded: HashSet[string]
|
|
84
|
|
85 for i in archive.walkFiles:
|
|
86 if i.startsWith(normDir):
|
|
87 let components = i[normDir.len .. ^1].split('/', maxsplit = 1)
|
|
88 if components.len == 1:
|
|
89 if not (components[0] in yielded):
|
|
90 yield (kind: pcFile, path: components[0])
|
|
91 else:
|
|
92 if not (components[0] in yielded):
|
|
93 yield (kind: pcDir, path: components[0])
|
|
94 yielded.incl components[0]
|
|
95 archive.close()
|
|
96
|
|
97 elif thebundletype == Exe:
|
|
98
|
|
99 import std/tables
|
|
100
|
|
101 const BUILD_RESOURCEROOT* {.strdefine.}: string = ""
|
|
102
|
|
103 proc loadResources(): Table[string, Table[string, string]] {.compileTime.} =
|
|
104 when BUILD_RESOURCEROOT == "":
|
|
105 {.warning: "BUILD_RESOURCEROOT is empty, no resources will be packaged".}
|
|
106 return
|
|
107 else:
|
|
108 for kind, packageDir in walkDir(BUILD_RESOURCEROOT):
|
|
109 if kind == pcDir:
|
|
110 let package = packageDir.splitPath.tail
|
|
111 result[package] = Table[string, string]()
|
|
112 for resourcefile in walkDirRec(packageDir, relative = true):
|
|
113 result[package][resourcefile.replace('\\', '/')] = staticRead(packageDir.joinPath(resourcefile))
|
|
114 const bundledResources = loadResources()
|
|
115
|
|
116 proc loadResource_intern(path: string, package: string): Stream =
|
|
117 if not (path in bundledResources[package]):
|
|
118 raise newException(Exception, &"Resource {path} not found")
|
|
119 newStringStream(bundledResources[package][path])
|
|
120
|
|
121 proc modList_intern(): seq[string] =
|
|
122 result = bundledResources.keys().toSeq()
|
|
123
|
|
124 iterator walkResources_intern(dir: string, package = DEFAULT_PACKAGE): string =
|
|
125 for i in bundledResources[package].keys:
|
|
126 yield i
|
|
127
|
|
128 iterator ls_intern(dir: string, package: string): tuple[kind: PathComponent, path: string] =
|
|
129 let normDir = dir.normalizeDir()
|
|
130 var yielded: HashSet[string]
|
|
131
|
|
132 for i in bundledResources[package].keys:
|
|
133 if i.startsWith(normDir):
|
|
134 let components = i[normDir.len .. ^1].split('/', maxsplit = 1)
|
|
135 if components.len == 1:
|
|
136 if not (components[0] in yielded):
|
|
137 yield (kind: pcFile, path: components[0])
|
|
138 else:
|
|
139 if not (components[0] in yielded):
|
|
140 yield (kind: pcDir, path: components[0])
|
|
141 yielded.incl components[0]
|
|
142
|
|
143 proc LoadResource*(path: string, package = DEFAULT_PACKAGE): Stream =
|
|
144 loadResource_intern(path, package = package)
|
|
145
|
|
146 #[
|
|
147 proc LoadImage*[T](path: string, package = DEFAULT_PACKAGE): Image[RGBAPixel] =
|
|
148 if path.splitFile().ext.toLowerAscii == ".bmp":
|
|
149 loadResource_intern(path, package = package).ReadBMP()
|
|
150 elif path.splitFile().ext.toLowerAscii == ".png":
|
|
151 loadResource_intern(path, package = package).ReadPNG()
|
|
152 else:
|
|
153 raise newException(Exception, "Unsupported image file type: " & path)
|
|
154
|
|
155 proc LoadJson*(path: string, package = DEFAULT_PACKAGE): JsonNode =
|
|
156 path.loadResource_intern(package = package).readAll().parseJson()
|
|
157
|
|
158 proc LoadConfig*(path: string, package = DEFAULT_PACKAGE): Config =
|
|
159 path.loadResource_intern(package = package).loadConfig(filename = path)
|
|
160
|
|
161 proc LoadFont*(
|
|
162 path: string,
|
|
163 name = "",
|
|
164 lineHeightPixels = 80'f32,
|
|
165 additional_codepoints: openArray[Rune] = [],
|
|
166 charset = ASCII_CHARSET,
|
|
167 package = DEFAULT_PACKAGE
|
|
168 ): Font =
|
|
169 var thename = name
|
|
170 if thename == "":
|
|
171 thename = path.splitFile().name
|
|
172 loadResource_intern(path, package = package).ReadTrueType(name, charset & additional_codepoints.toSeq, lineHeightPixels)
|
|
173
|
|
174 proc LoadMeshes*(path: string, defaultMaterial: MaterialType, package = DEFAULT_PACKAGE): seq[MeshTree] =
|
|
175 loadResource_intern(path, package = package).ReadglTF(defaultMaterial)
|
|
176
|
|
177 proc LoadFirstMesh*(path: string, defaultMaterial: MaterialType, package = DEFAULT_PACKAGE): Mesh =
|
|
178 loadResource_intern(path, package = package).ReadglTF(defaultMaterial)[0].toSeq[0]
|
|
179
|
|
180 ]#
|
|
181
|
|
182 proc Packages*(): seq[string] =
|
|
183 modList_intern()
|
|
184
|
|
185 proc WalkResources*(dir = "", package = DEFAULT_PACKAGE): seq[string] =
|
|
186 for i in walkResources_intern(dir, package = package):
|
|
187 if i.startsWith(dir):
|
|
188 result.add i
|
|
189 result.sort()
|
|
190
|
|
191 proc List*(dir: string, package = DEFAULT_PACKAGE): seq[tuple[kind: PathComponent, path: string]] =
|
|
192 for i in ls_intern(dir = dir, package = package):
|
|
193 result.add i
|
|
194 result.sort()
|