diff semicongine/resources.nim @ 840:44ec744fbedc

did: package restructuring according to nimble recommendation for libraries
author Sam <sam@basx.dev>
date Sat, 02 Dec 2023 22:23:29 +0700
parents
children 1ee397815b0b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/semicongine/resources.nim	Sat Dec 02 22:23:29 2023 +0700
@@ -0,0 +1,149 @@
+import std/streams
+import std/strutils
+import std/strformat
+import std/os
+import std/unicode
+
+import ./core
+import ./resources/image
+import ./resources/audio
+import ./resources/mesh
+import ./resources/font
+import ./mesh
+import ./material
+
+export image
+export audio
+export mesh
+
+type
+  ResourceBundlingType = enum
+    Dir # Directories
+    Zip # Zip files
+    Exe # Embeded in executable
+
+const thebundletype = parseEnum[ResourceBundlingType](BUNDLETYPE.toLowerAscii().capitalizeAscii())
+var selectedMod* = "default"
+
+# resource loading
+
+when thebundletype == Dir:
+
+  proc resourceRoot(): string =
+    joinPath(absolutePath(getAppDir()), RESOURCEROOT)
+  proc modRoot(): string =
+    joinPath(resourceRoot(), selectedMod)
+
+  proc loadResource_intern(path: string): Stream =
+    let realpath = joinPath(modRoot(), path)
+    if not realpath.fileExists():
+      raise newException(Exception, &"Resource {path} not found")
+    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(): string =
+    for file in walkDirRec(modRoot(), relative=true):
+      yield file
+
+elif thebundletype == Zip:
+
+  import zippy/ziparchives
+
+  proc resourceRoot(): string =
+    joinPath(absolutePath(getAppDir()), RESOURCEROOT)
+  proc modRoot(): string =
+    joinPath(resourceRoot(), selectedMod)
+
+  proc loadResource_intern(path: string): Stream =
+    if not path.fileExists():
+      raise newException(Exception, &"Resource {path} not found")
+    let archive = openZipArchive(modRoot() & ".zip")
+    # read all here so we can close the stream
+    result = newStringStream(archive.extractFile(path))
+    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(): string =
+    let archive = openZipArchive(modRoot() & ".zip")
+    for i in archive.walkFiles:
+      if i[^1] != '/':
+        yield i
+    archive.close()
+
+elif thebundletype == Exe:
+
+  import std/compilesettings
+  import std/tables
+  import std/sequtils
+
+  proc loadResources(): Table[string, Table[string, string]] {.compileTime.} =
+
+    let srcdir = joinPath(parentDir(querySetting(projectFull)), RESOURCEROOT)
+    for kind, moddir in walkDir(srcdir):
+      if kind == pcDir:
+        let modname = moddir.splitPath.tail
+        result[modname] = Table[string, string]()
+        for resourcefile in walkDirRec(moddir, relative=true):
+        # TODO: add Lempel–Ziv–Welch compression or something similar simple
+          result[modname][resourcefile] = staticRead(joinPath(moddir, resourcefile))
+  const bundledResources = loadResources()
+
+  proc loadResource_intern(path: string): Stream =
+    if not (path in bundledResources[selectedMod]):
+      raise newException(Exception, &"Resource {path} not found")
+    newStringStream(bundledResources[selectedMod][path])
+
+  proc modList_intern(): seq[string] =
+    result = bundledResources.keys().toSeq()
+
+  iterator walkResources_intern(): string =
+    for i in bundledResources[selectedMod].keys:
+      yield i
+
+proc loadResource*(path: string): Stream =
+  loadResource_intern(path)
+
+proc loadImage*(path: string): Image =
+  if path.splitFile().ext.toLowerAscii == ".bmp":
+    loadResource_intern(path).readBMP()
+  elif path.splitFile().ext.toLowerAscii == ".png":
+    loadResource_intern(path).readPNG()
+  else:
+    raise newException(Exception, "Unsupported image file type: " & path)
+
+proc loadAudio*(path: string): Sound =
+  if path.splitFile().ext.toLowerAscii == ".au":
+    loadResource_intern(path).readAU()
+  elif path.splitFile().ext.toLowerAscii == ".ogg":
+    loadResource_intern(path).readVorbis()
+  else:
+    raise newException(Exception, "Unsupported audio file type: " & path)
+
+proc loadFont*(path: string, name="", color=newVec4f(1, 1, 1, 1), resolution=100'f32): Font =
+  var thename = name
+  if thename == "":
+    thename = path.splitFile().name
+  let defaultCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=+[{]};:,<.>/? $!@#%^&*()\"'".toRunes()
+  loadResource_intern(path).readTrueType(name, defaultCharset, color, resolution)
+
+proc loadMeshes*(path: string, defaultMaterial: MaterialType): seq[MeshTree] =
+  loadResource_intern(path).readglTF(defaultMaterial)
+
+proc loadFirstMesh*(path: string, defaultMaterial: MaterialType): Mesh =
+  loadResource_intern(path).readglTF(defaultMaterial)[0].toSeq[0]
+
+proc modList*(): seq[string] =
+  modList_intern()
+
+iterator walkResources*(dir=""): string =
+  for i in walkResources_intern():
+    if i.startsWith(dir):
+      yield i