comparison semiconginev2/resources.nim @ 1225:27cd1c21290e compiletime-tests

did: refactor resources
author sam <sam@basx.dev>
date Wed, 17 Jul 2024 22:20:59 +0700
parents
children 5dcb503ef0c0
comparison
equal deleted inserted replaced
1224:a3fa15c25026 1225:27cd1c21290e
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()