comparison semiconginev2/old/resources.nim @ 1218:56781cc0fc7c compiletime-tests

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