Mercurial > games > semicongine
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() |