diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/semiconginev2/resources.nim	Wed Jul 17 22:20:59 2024 +0700
@@ -0,0 +1,194 @@
+type
+  ResourceBundlingType = enum
+    Dir # Directories
+    Zip # Zip files
+    Exe # Embeded in executable
+
+const
+  thebundletype = parseEnum[ResourceBundlingType](PACKAGETYPE.toLowerAscii().capitalizeAscii())
+  ASCII_CHARSET = PrintableChars.toSeq.toRunes
+  DEFAULT_PACKAGE = "default"
+
+# resource loading
+
+func normalizeDir(dir: string): string =
+  result = dir
+  if result.startsWith("./"):
+    result = result[2 .. ^1]
+  if result.startsWith("/"):
+    result = result[1 .. ^1]
+  result = dir.replace('\\', '/')
+  if not result.endsWith("/") and result != "":
+    result = result & "/"
+
+when thebundletype == Dir:
+
+  proc resourceRoot(): string =
+    getAppDir().absolutePath().joinPath(RESOURCEROOT)
+  proc packageRoot(package: string): string =
+    resourceRoot().joinPath(package)
+
+  proc loadResource_intern(path: string, package: string): Stream =
+    let realpath = package.packageRoot().joinPath(path)
+    if not realpath.fileExists():
+      raise newException(Exception, &"Resource {path} not found (checked {realpath})")
+    newFileStream(realpath, fmRead)
+
+  proc modList_intern(): seq[string] =
+    for kind, file in walkDir(resourceRoot(), relative = true):
+      if kind == pcDir:
+        result.add file
+
+  iterator walkResources_intern(dir: string, package = DEFAULT_PACKAGE): string =
+    for file in walkDirRec(package.packageRoot().joinPath(dir), relative = true):
+      yield file
+
+  iterator ls_intern(dir: string, package: string): tuple[kind: PathComponent, path: string] =
+    for i in walkDir(package.packageRoot().joinPath(dir), relative = true):
+      yield i
+
+elif thebundletype == Zip:
+
+  import ./thirdparty/zippy/zippy/ziparchives
+
+  proc resourceRoot(): string =
+    absolutePath(getAppDir()).joinPath(RESOURCEROOT)
+  proc packageRoot(package: string): string =
+    resourceRoot().joinPath(package)
+
+  proc loadResource_intern(path: string, package: string): Stream =
+    let archive = openZipArchive(package.packageRoot() & ".zip")
+    try:
+      result = newStringStream(archive.extractFile(path))
+    except ZippyError:
+      raise newException(Exception, &"Resource {path} not found")
+    archive.close()
+
+  proc modList_intern(): seq[string] =
+    for kind, file in walkDir(resourceRoot(), relative = true):
+      if kind == pcFile and file.endsWith(".zip"):
+        result.add file[0 ..< ^4]
+
+  iterator walkResources_intern(dir: string, package = DEFAULT_PACKAGE): string =
+    let archive = openZipArchive(package.packageRoot() & ".zip")
+    let normDir = dir.normalizeDir()
+    for i in archive.walkFiles:
+      if i.startsWith(normDir):
+        yield i
+    archive.close()
+
+  iterator ls_intern(dir: string, package: string): tuple[kind: PathComponent, path: string] =
+    let archive = openZipArchive(package.packageRoot() & ".zip")
+    let normDir = dir.normalizeDir()
+    var yielded: HashSet[string]
+
+    for i in archive.walkFiles:
+      if i.startsWith(normDir):
+        let components = i[normDir.len .. ^1].split('/', maxsplit = 1)
+        if components.len == 1:
+          if not (components[0] in yielded):
+            yield (kind: pcFile, path: components[0])
+        else:
+          if not (components[0] in yielded):
+            yield (kind: pcDir, path: components[0])
+        yielded.incl components[0]
+    archive.close()
+
+elif thebundletype == Exe:
+
+  import std/tables
+
+  const BUILD_RESOURCEROOT* {.strdefine.}: string = ""
+
+  proc loadResources(): Table[string, Table[string, string]] {.compileTime.} =
+    when BUILD_RESOURCEROOT == "":
+      {.warning: "BUILD_RESOURCEROOT is empty, no resources will be packaged".}
+      return
+    else:
+      for kind, packageDir in walkDir(BUILD_RESOURCEROOT):
+        if kind == pcDir:
+          let package = packageDir.splitPath.tail
+          result[package] = Table[string, string]()
+          for resourcefile in walkDirRec(packageDir, relative = true):
+            result[package][resourcefile.replace('\\', '/')] = staticRead(packageDir.joinPath(resourcefile))
+  const bundledResources = loadResources()
+
+  proc loadResource_intern(path: string, package: string): Stream =
+    if not (path in bundledResources[package]):
+      raise newException(Exception, &"Resource {path} not found")
+    newStringStream(bundledResources[package][path])
+
+  proc modList_intern(): seq[string] =
+    result = bundledResources.keys().toSeq()
+
+  iterator walkResources_intern(dir: string, package = DEFAULT_PACKAGE): string =
+    for i in bundledResources[package].keys:
+      yield i
+
+  iterator ls_intern(dir: string, package: string): tuple[kind: PathComponent, path: string] =
+    let normDir = dir.normalizeDir()
+    var yielded: HashSet[string]
+
+    for i in bundledResources[package].keys:
+      if i.startsWith(normDir):
+        let components = i[normDir.len .. ^1].split('/', maxsplit = 1)
+        if components.len == 1:
+          if not (components[0] in yielded):
+            yield (kind: pcFile, path: components[0])
+        else:
+          if not (components[0] in yielded):
+            yield (kind: pcDir, path: components[0])
+        yielded.incl components[0]
+
+proc LoadResource*(path: string, package = DEFAULT_PACKAGE): Stream =
+  loadResource_intern(path, package = package)
+
+#[
+proc LoadImage*[T](path: string, package = DEFAULT_PACKAGE): Image[RGBAPixel] =
+  if path.splitFile().ext.toLowerAscii == ".bmp":
+    loadResource_intern(path, package = package).ReadBMP()
+  elif path.splitFile().ext.toLowerAscii == ".png":
+    loadResource_intern(path, package = package).ReadPNG()
+  else:
+    raise newException(Exception, "Unsupported image file type: " & path)
+
+proc LoadJson*(path: string, package = DEFAULT_PACKAGE): JsonNode =
+  path.loadResource_intern(package = package).readAll().parseJson()
+
+proc LoadConfig*(path: string, package = DEFAULT_PACKAGE): Config =
+  path.loadResource_intern(package = package).loadConfig(filename = path)
+
+proc LoadFont*(
+  path: string,
+  name = "",
+  lineHeightPixels = 80'f32,
+  additional_codepoints: openArray[Rune] = [],
+  charset = ASCII_CHARSET,
+  package = DEFAULT_PACKAGE
+): Font =
+  var thename = name
+  if thename == "":
+    thename = path.splitFile().name
+  loadResource_intern(path, package = package).ReadTrueType(name, charset & additional_codepoints.toSeq, lineHeightPixels)
+
+proc LoadMeshes*(path: string, defaultMaterial: MaterialType, package = DEFAULT_PACKAGE): seq[MeshTree] =
+  loadResource_intern(path, package = package).ReadglTF(defaultMaterial)
+
+proc LoadFirstMesh*(path: string, defaultMaterial: MaterialType, package = DEFAULT_PACKAGE): Mesh =
+  loadResource_intern(path, package = package).ReadglTF(defaultMaterial)[0].toSeq[0]
+
+]#
+
+proc Packages*(): seq[string] =
+  modList_intern()
+
+proc WalkResources*(dir = "", package = DEFAULT_PACKAGE): seq[string] =
+  for i in walkResources_intern(dir, package = package):
+    if i.startsWith(dir):
+      result.add i
+  result.sort()
+
+proc List*(dir: string, package = DEFAULT_PACKAGE): seq[tuple[kind: PathComponent, path: string]] =
+  for i in ls_intern(dir = dir, package = package):
+    result.add i
+  result.sort()