asset archiving wip, font update

This commit is contained in:
minjaesong
2026-02-19 09:53:32 +09:00
parent 661d516800
commit 04b49b8a5c
80 changed files with 617 additions and 336 deletions

View File

@@ -0,0 +1,10 @@
wireItemID;const
wire@dwarventech:1;0.995
wire@dwarventech:2;0.995
wire@dwarventech:3;0.995
wire@dwarventech:4;0.995
wire@dwarventech:5;0.995
wire@dwarventech:6;0.999
wire@dwarventech:7;0.999
wire@dwarventech:8;0.999
1 wireItemID const
2 wire@dwarventech:1 0.995
3 wire@dwarventech:2 0.995
4 wire@dwarventech:3 0.995
5 wire@dwarventech:4 0.995
6 wire@dwarventech:5 0.995
7 wire@dwarventech:6 0.999
8 wire@dwarventech:7 0.999
9 wire@dwarventech:8 0.999

View File

@@ -5,6 +5,7 @@ what:
assets: assets:
./make_assets_release.sh || true ./make_assets_release.sh || true
./make_assets_archive.sh
linux_x86: linux_x86:
./build_app_linux_x86.sh ./build_app_linux_x86.sh

View File

@@ -9,8 +9,8 @@ RUNTIME="runtime-linux-arm"
DESKTOPFILE="../out/build_autogen_linux.desktop" DESKTOPFILE="../out/build_autogen_linux.desktop"
JARNAME="TerrarumBuild.jar" JARNAME="TerrarumBuild.jar"
if [ ! -f "out/assets.tar.zst" ] || [ ! -f "out/assets.manifest" ]; then if [ ! -f "out/assets.tevd" ]; then
echo "'assets.tar.zst' or 'assets.manifest' not found in out/; run 'make assets' first." >&2 echo "'assets.tevd' not found in out/; run 'make assets' first." >&2
exit 1 exit 1
fi fi
@@ -29,9 +29,8 @@ mkdir $DESTDIR/out
cp -r "../out/$RUNTIME" $DESTDIR/out/ cp -r "../out/$RUNTIME" $DESTDIR/out/
mv $DESTDIR/out/$RUNTIME/bin/java $DESTDIR/out/$RUNTIME/bin/java mv $DESTDIR/out/$RUNTIME/bin/java $DESTDIR/out/$RUNTIME/bin/java
# Copy over the asset archive, manifest, and jarfile # Copy over the asset archive and jarfile
cp "out/assets.tar.zst" $DESTDIR/ cp "out/assets.tevd" $DESTDIR/
cp "out/assets.manifest" $DESTDIR/
cp "../out/$JARNAME" $DESTDIR/out/ cp "../out/$JARNAME" $DESTDIR/out/
# Pack everything to AppImage # Pack everything to AppImage

View File

@@ -9,8 +9,8 @@ RUNTIME="runtime-linux-x86"
DESKTOPFILE="../out/build_autogen_linux.desktop" DESKTOPFILE="../out/build_autogen_linux.desktop"
JARNAME="TerrarumBuild.jar" JARNAME="TerrarumBuild.jar"
if [ ! -f "out/assets.tar.zst" ] || [ ! -f "out/assets.manifest" ]; then if [ ! -f "out/assets.tevd" ]; then
echo "'assets.tar.zst' or 'assets.manifest' not found in out/; run 'make assets' first." >&2 echo "'assets.tevd' not found in out/; run 'make assets' first." >&2
exit 1 exit 1
fi fi
@@ -29,9 +29,8 @@ mkdir $DESTDIR/out
cp -r "../out/$RUNTIME" $DESTDIR/out/ cp -r "../out/$RUNTIME" $DESTDIR/out/
mv $DESTDIR/out/$RUNTIME/bin/java $DESTDIR/out/$RUNTIME/bin/java mv $DESTDIR/out/$RUNTIME/bin/java $DESTDIR/out/$RUNTIME/bin/java
# Copy over the asset archive, manifest, and jarfile # Copy over the asset archive and jarfile
cp "out/assets.tar.zst" $DESTDIR/ cp "out/assets.tevd" $DESTDIR/
cp "out/assets.manifest" $DESTDIR/
cp "../out/$JARNAME" $DESTDIR/out/ cp "../out/$JARNAME" $DESTDIR/out/
# Pack everything to AppImage # Pack everything to AppImage

View File

@@ -9,8 +9,8 @@ RUNTIME="runtime-osx-arm"
PLISTFILE="../out/build_autogen_macos_Info.plist" PLISTFILE="../out/build_autogen_macos_Info.plist"
JARNAME="TerrarumBuild.jar" JARNAME="TerrarumBuild.jar"
if [ ! -f "out/assets.tar.zst" ] || [ ! -f "out/assets.manifest" ]; then if [ ! -f "out/assets.tevd" ]; then
echo "'assets.tar.zst' or 'assets.manifest' not found in out/; run 'make assets' first." >&2 echo "'assets.tevd' not found in out/; run 'make assets' first." >&2
exit 1 exit 1
fi fi
@@ -32,9 +32,8 @@ mkdir $DESTDIR/Contents/MacOS/out
cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/out/ cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/out/
mv $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java mv $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java
# Copy over the asset archive, manifest, and jarfile # Copy over the asset archive and jarfile
cp "out/assets.tar.zst" $DESTDIR/Contents/MacOS/ cp "out/assets.tevd" $DESTDIR/Contents/MacOS/
cp "out/assets.manifest" $DESTDIR/Contents/MacOS/
cp "../out/$JARNAME" $DESTDIR/Contents/MacOS/out/ cp "../out/$JARNAME" $DESTDIR/Contents/MacOS/out/
# zip everything # zip everything

View File

@@ -9,8 +9,8 @@ RUNTIME="runtime-osx-x86"
PLISTFILE="../out/build_autogen_macos_Info.plist" PLISTFILE="../out/build_autogen_macos_Info.plist"
JARNAME="TerrarumBuild.jar" JARNAME="TerrarumBuild.jar"
if [ ! -f "out/assets.tar.zst" ] || [ ! -f "out/assets.manifest" ]; then if [ ! -f "out/assets.tevd" ]; then
echo "'assets.tar.zst' or 'assets.manifest' not found in out/; run 'make assets' first." >&2 echo "'assets.tevd' not found in out/; run 'make assets' first." >&2
exit 1 exit 1
fi fi
@@ -32,9 +32,8 @@ mkdir $DESTDIR/Contents/MacOS/out
cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/out/ cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/out/
mv $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java mv $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java $DESTDIR/Contents/MacOS/out/$RUNTIME/bin/java
# Copy over the asset archive, manifest, and jarfile # Copy over the asset archive and jarfile
cp "out/assets.tar.zst" $DESTDIR/Contents/MacOS/ cp "out/assets.tevd" $DESTDIR/Contents/MacOS/
cp "out/assets.manifest" $DESTDIR/Contents/MacOS/
cp "../out/$JARNAME" $DESTDIR/Contents/MacOS/out/ cp "../out/$JARNAME" $DESTDIR/Contents/MacOS/out/
# zip everything # zip everything

View File

@@ -8,8 +8,8 @@ RUNTIME="runtime-windows-x86"
RCFILE="../out/build_autogen_windows.rc" RCFILE="../out/build_autogen_windows.rc"
JARNAME="TerrarumBuild.jar" JARNAME="TerrarumBuild.jar"
if [ ! -f "out/assets.tar.zst" ] || [ ! -f "out/assets.manifest" ]; then if [ ! -f "out/assets.tevd" ]; then
echo "'assets.tar.zst' or 'assets.manifest' not found in out/; run 'make assets' first." >&2 echo "'assets.tevd' not found in out/; run 'make assets' first." >&2
exit 1 exit 1
fi fi
@@ -34,9 +34,8 @@ mkdir $DESTDIR/out
cp -r "../out/$RUNTIME" $DESTDIR/out/ cp -r "../out/$RUNTIME" $DESTDIR/out/
mv $DESTDIR/out/$RUNTIME/bin/java.exe $DESTDIR/out/$RUNTIME/bin/java.exe mv $DESTDIR/out/$RUNTIME/bin/java.exe $DESTDIR/out/$RUNTIME/bin/java.exe
# Copy over the asset archive, manifest, and jarfile # Copy over the asset archive and jarfile
cp "out/assets.tar.zst" $DESTDIR/ cp "out/assets.tevd" $DESTDIR/
cp "out/assets.manifest" $DESTDIR/
cp "../out/$JARNAME" $DESTDIR/out/ cp "../out/$JARNAME" $DESTDIR/out/
# zip everything # zip everything

View File

@@ -0,0 +1,43 @@
#!/bin/bash
if (( $EUID == 0 )); then echo "The build process is not meant to be run with root privilege, exiting now." >&2; exit 1; fi
cd "${0%/*}"
SRCDIR="../assets_release"
OUTDIR="out"
JARNAME="TerrarumBuild.jar"
TVDJAR="../lib/TerranVirtualDisk.jar"
if [ ! -d "$SRCDIR" ]; then
echo "Error: $SRCDIR does not exist. Run make_assets_release.sh first." >&2
exit 1
fi
if [ ! -f "../out/$JARNAME" ]; then
echo "Error: ../out/$JARNAME not found. Build the project first." >&2
exit 1
fi
if [ ! -f "$TVDJAR" ]; then
echo "Error: $TVDJAR not found." >&2
exit 1
fi
mkdir -p "$OUTDIR"
echo "Creating assets.tevd from $SRCDIR..."
# Build classpath from project JAR and all library JARs
CP="../out/$JARNAME"
for jar in ../lib/*.jar; do
CP="$CP:$jar"
done
java -cp "$CP" net.torvald.terrarum.AssetArchiveBuilderKt "$SRCDIR" "$OUTDIR/assets.tevd"
if [ $? -ne 0 ]; then
echo "Error: Failed to create assets.tevd" >&2
exit 1
fi
echo "Done. Output:"
echo " $OUTDIR/assets.tevd ($(du -h "$OUTDIR/assets.tevd" | cut -f1))"

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2017,15 +2017,15 @@ object BTeXParser {
private lateinit var subtitleFont: TerrarumSansBitmap private lateinit var subtitleFont: TerrarumSansBitmap
fun preloadFonts() { fun preloadFonts() {
testFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha, textCacheSize = 4096) testFont = TerrarumSansBitmap(shadowAlpha = bodyTextShadowAlpha, textCacheSize = 4096)
partTitleFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha).also { partTitleFont = TerrarumSansBitmap(shadowAlpha = bodyTextShadowAlpha).also {
it.interchar = 1 it.interchar = 1
} }
titleFont = TerrarumSansBitmap(App.FONT_DIR).also { titleFont = TerrarumSansBitmap().also {
it.interchar = 1 it.interchar = 1
it.scale = 2 it.scale = 2
} }
subtitleFont = TerrarumSansBitmap(App.FONT_DIR).also { subtitleFont = TerrarumSansBitmap().also {
it.interchar = 1 it.interchar = 1
} }
fontInit = true fontInit = true

View File

@@ -255,10 +255,6 @@ public class App implements ApplicationListener {
public static DebugTimers debugTimers = new DebugTimers(); public static DebugTimers debugTimers = new DebugTimers();
public static final String FONT_DIR = "assets/graphics/fonts/terrarum-sans-bitmap";
public static Texture[] ditherPatterns = new Texture[4]; public static Texture[] ditherPatterns = new Texture[4];
// public static ShaderProgram shaderHicolour; // public static ShaderProgram shaderHicolour;
public static ShaderProgram shaderDebugDiff; public static ShaderProgram shaderDebugDiff;
@@ -445,6 +441,7 @@ public class App implements ApplicationListener {
// load configs // load configs
getDefaultDirectory(); getDefaultDirectory();
createDirs(); createDirs();
AssetCache.INSTANCE.init();
initialiseConfig(); initialiseConfig();
readConfigJson(); readConfigJson();
@@ -582,13 +579,13 @@ public class App implements ApplicationListener {
if (!loadOrder.isEmpty()) { if (!loadOrder.isEmpty()) {
var modname = loadOrder.get(0).get(0); var modname = loadOrder.get(0).get(0);
var textureFile = Gdx.files.internal("assets/mods/"+modname+"/splashback.png"); var textureFile = AssetCache.INSTANCE.getFileHandle("mods/"+modname+"/splashback.png");
if (textureFile.exists()) { if (textureFile.exists()) {
splashBackdrop = new TextureRegion(new Texture(textureFile)); splashBackdrop = new TextureRegion(new Texture(textureFile));
splashTextCol = new Color(0xeeeeeeff); splashTextCol = new Color(0xeeeeeeff);
} }
var logoFile = Gdx.files.internal("assets/mods/"+modname+"/splashlogo.png"); var logoFile = AssetCache.INSTANCE.getFileHandle("mods/"+modname+"/splashlogo.png");
if (logoFile.exists()) { if (logoFile.exists()) {
splashScreenLogo = new TextureRegion(new Texture(logoFile)); splashScreenLogo = new TextureRegion(new Texture(logoFile));
} }
@@ -604,10 +601,10 @@ public class App implements ApplicationListener {
} }
if (splashBackdrop == null) { if (splashBackdrop == null) {
splashBackdrop = new TextureRegion(new Texture("assets/graphics/background_white.png")); splashBackdrop = new TextureRegion(new Texture(AssetCache.INSTANCE.getFileHandle("graphics/background_white.png")));
} }
if (splashScreenLogo == null) { if (splashScreenLogo == null) {
splashScreenLogo = new TextureRegion(new Texture("assets/graphics/logo.png")); splashScreenLogo = new TextureRegion(new Texture(AssetCache.INSTANCE.getFileHandle("graphics/logo.png")));
} }
Gdx.graphics.setContinuousRendering(true); Gdx.graphics.setContinuousRendering(true);
@@ -622,13 +619,13 @@ public class App implements ApplicationListener {
ShaderMgr.INSTANCE.compile(Gdx.files.classpath("shaders/shaders.csv")); ShaderMgr.INSTANCE.compile(Gdx.files.classpath("shaders/shaders.csv"));
CommonResourcePool.INSTANCE.addToLoadingList("title_health1", () -> new Texture(Gdx.files.internal("./assets/graphics/gui/health_take_a_break.tga"))); CommonResourcePool.INSTANCE.addToLoadingList("title_health1", () -> new Texture(AssetCache.INSTANCE.getFileHandle("graphics/gui/health_take_a_break.tga")));
CommonResourcePool.INSTANCE.addToLoadingList("title_health2", () -> new Texture(Gdx.files.internal("./assets/graphics/gui/health_distance.tga"))); CommonResourcePool.INSTANCE.addToLoadingList("title_health2", () -> new Texture(AssetCache.INSTANCE.getFileHandle("graphics/gui/health_distance.tga")));
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bup", () -> new MusicContainer("haptic_bup", Gdx.files.internal("./assets/audio/effects/haptic_bup.ogg").file(), false, true, (AudioBank m) -> { return null; })); CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bup", () -> new MusicContainer("haptic_bup", AssetCache.INSTANCE.getFileHandle("audio/effects/haptic_bup.ogg"), false, true, null, (AudioBank m) -> { return null; }));
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bap", () -> new MusicContainer("haptic_bap", Gdx.files.internal("./assets/audio/effects/haptic_bap.ogg").file(), false, true, (AudioBank m) -> { return null; })); CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bap", () -> new MusicContainer("haptic_bap", AssetCache.INSTANCE.getFileHandle("audio/effects/haptic_bap.ogg"), false, true, null, (AudioBank m) -> { return null; }));
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bop", () -> new MusicContainer("haptic_bop", Gdx.files.internal("./assets/audio/effects/haptic_bop.ogg").file(), false, true, (AudioBank m) -> { return null; })); CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bop", () -> new MusicContainer("haptic_bop", AssetCache.INSTANCE.getFileHandle("audio/effects/haptic_bop.ogg"), false, true, null, (AudioBank m) -> { return null; }));
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bep", () -> new MusicContainer("haptic_bep", Gdx.files.internal("./assets/audio/effects/haptic_bep.ogg").file(), false, true, (AudioBank m) -> { return null; })); CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bep", () -> new MusicContainer("haptic_bep", AssetCache.INSTANCE.getFileHandle("audio/effects/haptic_bep.ogg"), false, true, null, (AudioBank m) -> { return null; }));
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bip", () -> new MusicContainer("haptic_bip", Gdx.files.internal("./assets/audio/effects/haptic_bip.ogg").file(), false, true, (AudioBank m) -> { highPrioritySoundPlaying = false; return null; })); CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bip", () -> new MusicContainer("haptic_bip", AssetCache.INSTANCE.getFileHandle("audio/effects/haptic_bip.ogg"), false, true, null, (AudioBank m) -> { highPrioritySoundPlaying = false; return null; }));
// make loading list // make loading list
CommonResourcePool.INSTANCE.loadAll(); CommonResourcePool.INSTANCE.loadAll();
@@ -669,7 +666,7 @@ public class App implements ApplicationListener {
rendererVendor = Gdx.graphics.getGLVersion().getVendorString(); rendererVendor = Gdx.graphics.getGLVersion().getVendorString();
fontGame = new TerrarumSansBitmap(FONT_DIR, false, false, false, fontGame = new TerrarumSansBitmap(false, false, false,
false, false,
256, false, 0.5f, false 256, false, 0.5f, false
); );
@@ -1062,6 +1059,8 @@ public class App implements ApplicationListener {
deleteTempfiles(); deleteTempfiles();
AssetCache.INSTANCE.dispose();
Toolkit.INSTANCE.dispose(); Toolkit.INSTANCE.dispose();
BlurMgr.INSTANCE.dispose(); BlurMgr.INSTANCE.dispose();
@@ -1157,11 +1156,11 @@ public class App implements ApplicationListener {
long t1 = System.nanoTime(); long t1 = System.nanoTime();
CommonResourcePool.INSTANCE.addToLoadingList("blockmarkings_common", () -> new TextureRegionPack(Gdx.files.internal("assets/graphics/blocks/block_markings_common.tga"), 16, 16, 0, 0, 0, 0, false, false, false)); CommonResourcePool.INSTANCE.addToLoadingList("blockmarkings_common", () -> new TextureRegionPack(AssetCache.INSTANCE.getFileHandle("graphics/blocks/block_markings_common.tga"), 16, 16, 0, 0, 0, 0, false, false, false));
CommonResourcePool.INSTANCE.addToLoadingList("blockmarking_actor", () -> new BlockMarkerActor()); CommonResourcePool.INSTANCE.addToLoadingList("blockmarking_actor", () -> new BlockMarkerActor());
CommonResourcePool.INSTANCE.addToLoadingList("loading_circle_64", () -> new TextureRegionPack(Gdx.files.internal("assets/graphics/gui/loading_circle_64.tga"), 64, 64, 0, 0, 0, 0, false, false, false)); CommonResourcePool.INSTANCE.addToLoadingList("loading_circle_64", () -> new TextureRegionPack(AssetCache.INSTANCE.getFileHandle("graphics/gui/loading_circle_64.tga"), 64, 64, 0, 0, 0, 0, false, false, false));
CommonResourcePool.INSTANCE.addToLoadingList("inline_loading_spinner", () -> new TextureRegionPack(Gdx.files.internal("assets/graphics/gui/inline_loading_spinner.tga"), 20, 20, 0, 0, 0, 0, false, false, false)); CommonResourcePool.INSTANCE.addToLoadingList("inline_loading_spinner", () -> new TextureRegionPack(AssetCache.INSTANCE.getFileHandle("graphics/gui/inline_loading_spinner.tga"), 20, 20, 0, 0, 0, 0, false, false, false));
CommonResourcePool.INSTANCE.addToLoadingList("inventory_category", () -> new TextureRegionPack("./assets/graphics/gui/inventory/category.tga", 20, 20, 0, 0, 0, 0, false, false, false)); CommonResourcePool.INSTANCE.addToLoadingList("inventory_category", () -> new TextureRegionPack(AssetCache.INSTANCE.getFileHandle("graphics/gui/inventory/category.tga"), 20, 20, 0, 0, 0, 0, false, false, false));
CommonResourcePool.INSTANCE.loadAll(); CommonResourcePool.INSTANCE.loadAll();
// shaderHicolour = loadShaderFromClasspath("shaders/default.vert", "shaders/hicolour.frag"); // shaderHicolour = loadShaderFromClasspath("shaders/default.vert", "shaders/hicolour.frag");
@@ -1226,12 +1225,12 @@ public class App implements ApplicationListener {
else { else {
environment = RunningEnvironment.PC; environment = RunningEnvironment.PC;
}*/ }*/
fontUITitle = new TerrarumSansBitmap(FONT_DIR, false, false, false, fontUITitle = new TerrarumSansBitmap(false, false, false,
false, false,
64, false, 0.5f, false 64, false, 0.5f, false
); );
fontUITitle.setInterchar(1); fontUITitle.setInterchar(1);
fontGameFBO = new TerrarumSansBitmap(FONT_DIR, false, true, false, fontGameFBO = new TerrarumSansBitmap(false, true, false,
false, false,
64, false, 203f/255f, false 64, false, 203f/255f, false
); );
@@ -1514,6 +1513,8 @@ public class App implements ApplicationListener {
/** defaultDir + "/Custom/Music" */ /** defaultDir + "/Custom/Music" */
public static String customMusicDir; public static String customMusicDir;
public static String customAmbientDir; public static String customAmbientDir;
/** defaultDir + "/Caches" */
public static String cachesDir;
private static void getDefaultDirectory() { private static void getDefaultDirectory() {
String OS = OSName.toUpperCase(); String OS = OSName.toUpperCase();
@@ -1551,6 +1552,7 @@ public class App implements ApplicationListener {
customDir = defaultDir + "/Custom"; customDir = defaultDir + "/Custom";
customMusicDir = customDir + "/Music"; customMusicDir = customDir + "/Music";
customAmbientDir = customDir + "/Ambient"; customAmbientDir = customDir + "/Ambient";
cachesDir = defaultDir + "/Caches";
System.out.println(String.format("os.name = %s (with identifier %s)", OSName, operationSystem)); System.out.println(String.format("os.name = %s (with identifier %s)", OSName, operationSystem));
System.out.println(String.format("os.version = %s", OSVersion)); System.out.println(String.format("os.version = %s", OSVersion));

View File

@@ -0,0 +1,59 @@
package net.torvald.terrarum
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustfile
import java.io.File
/**
* Build-time tool that creates assets.tevd from assets_release/ directory.
*
* Usage: java -cp <classpath> net.torvald.terrarum.AssetArchiveBuilderKt [assets_release_dir] [output_file]
*/
fun main(args: Array<String>) {
val srcDir = File(if (args.isNotEmpty()) args[0] else "assets_release")
val outFile = File(if (args.size > 1) args[1] else "out/assets.tevd")
if (!srcDir.exists() || !srcDir.isDirectory) {
System.err.println("Error: Source directory '${srcDir.path}' does not exist or is not a directory.")
System.exit(1)
}
outFile.parentFile?.mkdirs()
if (outFile.exists()) outFile.delete()
println("Scanning $srcDir...")
var totalSize = 0L
var fileCount = 0
srcDir.walkTopDown().filter { it.isFile }.forEach {
totalSize += it.length()
fileCount++
}
// Calculate capacity in sectors (4096 bytes per sector/cluster)
// Add overhead for FAT entries and directory structures
val clusterSize = ClusteredFormatDOM.CLUSTER_SIZE
val sectorsForData = (totalSize + clusterSize - 1) / clusterSize
// Add ~25% overhead for FAT, directory entries, and slack space
val capacityInSectors = ((sectorsForData * 1.25) + fileCount + 256).toLong()
.coerceAtMost(ClusteredFormatDOM.MAX_CAPA_IN_SECTORS.toLong())
.toInt()
println(" Files: $fileCount")
println(" Total size: ${totalSize / 1024} KB")
println(" Allocating $capacityInSectors sectors...")
println("Creating archive: ${outFile.path}")
val diskArchive = ClusteredFormatDOM.createNewArchive(outFile, Charsets.UTF_8, "Terrarum Assets", capacityInSectors)
val dom = ClusteredFormatDOM(diskArchive)
println("Importing files from ${srcDir.path}...")
val root = Clustfile(dom, "/")
root.importFrom(srcDir)
println("Trimming archive...")
dom.trimArchive()
dom.dispose()
println("Done. Output:")
println(" ${outFile.path} (${outFile.length() / 1024} KB, $fileCount files)")
}

View File

@@ -0,0 +1,68 @@
package net.torvald.terrarum
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustfile
import java.io.File
import java.io.RandomAccessFile
/**
* Central accessor for the TerranVirtualDisk asset archive.
* In distribution mode, assets are read directly from assets.tevd.
* In development mode, assets are read from the local ./assets/ directory.
*/
object AssetCache {
private val archivePath = File("./assets.tevd")
/** Whether we're running from a distribution archive */
val isDistribution: Boolean get() = archivePath.exists()
private var dom: ClusteredFormatDOM? = null
/**
* Open the archive on startup. Call early, after defaultDir is set.
*/
fun init() {
if (isDistribution) {
println("[AssetCache] Distribution mode: opening ${archivePath.path}")
dom = ClusteredFormatDOM(RandomAccessFile(archivePath, "r"))
println("[AssetCache] Archive opened successfully")
} else {
println("[AssetCache] No archive found, using loose assets (development mode)")
}
}
/**
* Get a Clustfile for a path relative to the assets root.
*/
fun getClustfile(relativePath: String): Clustfile {
val path = if (relativePath.startsWith("/")) relativePath else "/$relativePath"
return Clustfile(dom!!, path)
}
/**
* Get a GDX FileHandle — returns ClustfileHandle in distribution, Gdx.files.internal in dev.
*/
fun getFileHandle(relativePath: String): FileHandle {
return if (isDistribution) ClustfileHandle(getClustfile(relativePath))
else Gdx.files.internal("./assets/$relativePath")
}
/**
* Resolve a path string. In dev mode returns local path; in distribution mode throws
* as callers should use getFileHandle() instead.
*/
fun resolve(relativePath: String): String {
return if (isDistribution) throw UnsupportedOperationException(
"Use AssetCache.getFileHandle(\"$relativePath\") in distribution mode"
)
else "./assets/$relativePath"
}
fun dispose() {
dom?.dispose()
dom = null
}
}

View File

@@ -0,0 +1,64 @@
package net.torvald.terrarum
import com.badlogic.gdx.files.FileHandle
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustfile
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClustfileInputStream
import java.io.File
import java.io.InputStream
/**
* A GDX FileHandle backed by a Clustfile from TerranVirtualDisk.
* Allows transparent asset loading from .tevd archives using all standard GDX APIs.
*/
class ClustfileHandle(private val clustfile: Clustfile) : FileHandle() {
override fun read(): InputStream = ClustfileInputStream(clustfile)
override fun readBytes(): ByteArray = clustfile.readBytes()
override fun readString(charset: String?): String = String(readBytes(), charset(charset ?: "UTF-8"))
override fun exists(): Boolean = clustfile.exists()
override fun length(): Long = clustfile.length()
override fun isDirectory(): Boolean = clustfile.isDirectory
override fun name(): String = clustfile.name
override fun path(): String = clustfile.path
override fun extension(): String {
val n = name()
val dotIndex = n.lastIndexOf('.')
return if (dotIndex == -1) "" else n.substring(dotIndex + 1)
}
override fun nameWithoutExtension(): String {
val n = name()
val dotIndex = n.lastIndexOf('.')
return if (dotIndex == -1) n else n.substring(0, dotIndex)
}
override fun list(): Array<FileHandle> {
return clustfile.listFiles()?.map { ClustfileHandle(it) }?.toTypedArray() ?: arrayOf()
}
override fun child(name: String): FileHandle {
val childPath = if (clustfile.path.endsWith("/")) "${clustfile.path}$name" else "${clustfile.path}/$name"
return ClustfileHandle(Clustfile(clustfile.DOM, childPath))
}
override fun parent(): FileHandle {
val parentFile = clustfile.parentFile
return if (parentFile != null) ClustfileHandle(parentFile)
else ClustfileHandle(Clustfile(clustfile.DOM, "/"))
}
override fun file(): File {
// Return a dummy File for logging/toString purposes only
return File(clustfile.path)
}
override fun toString(): String = clustfile.path
}

View File

@@ -103,7 +103,7 @@ object ModMgr {
NOT_EVEN_THERE NOT_EVEN_THERE
} }
const val modDirInternal = "./assets/mods" val modDirInternal: String get() = "./assets/mods"
val modDirExternal = "${App.defaultDir}/Modules" val modDirExternal = "${App.defaultDir}/Modules"
/** Module name (directory name), ModuleMetadata */ /** Module name (directory name), ModuleMetadata */
@@ -161,21 +161,46 @@ object ModMgr {
try { try {
val modMetadata = Properties() val modMetadata = Properties()
val _internalFile = File("$modDirInternal/$moduleName/$metaFilename")
val _externalFile = File("$modDirExternal/$moduleName/$metaFilename") val _externalFile = File("$modDirExternal/$moduleName/$metaFilename")
// external mod has precedence over the internal // external mod has precedence over the internal
val isInternal = if (_externalFile.exists()) false else if (_internalFile.exists()) true else throw FileNotFoundException() val internalExists = if (AssetCache.isDistribution)
val file = if (isInternal) _internalFile else _externalFile AssetCache.getFileHandle("mods/$moduleName/$metaFilename").exists()
else
File("$modDirInternal/$moduleName/$metaFilename").exists()
val isInternal = if (_externalFile.exists()) false else if (internalExists) true else throw FileNotFoundException()
val modDir = if (isInternal) modDirInternal else modDirExternal val modDir = if (isInternal) modDirInternal else modDirExternal
fun getGdxFile(path: String) = if (isInternal) Gdx.files.internal(path) else Gdx.files.absolute(path) fun getGdxFileLocal(path: String) = if (isInternal) {
if (AssetCache.isDistribution) AssetCache.getFileHandle(path.removePrefix("./assets/"))
else Gdx.files.internal(path)
} else Gdx.files.absolute(path)
modMetadata.load(FileInputStream(file)) // Load metadata
if (isInternal && AssetCache.isDistribution) {
val metaHandle = AssetCache.getFileHandle("mods/$moduleName/$metaFilename")
modMetadata.load(metaHandle.read())
} else {
val file = if (isInternal) File("$modDirInternal/$moduleName/$metaFilename") else _externalFile
modMetadata.load(FileInputStream(file))
}
if (File("$modDir/$moduleName/$defaultConfigFilename").exists()) { // Load default config
val defaultConfigHandle = if (isInternal && AssetCache.isDistribution)
AssetCache.getFileHandle("mods/$moduleName/$defaultConfigFilename")
else
null
val defaultConfigExists = if (defaultConfigHandle != null)
defaultConfigHandle.exists()
else
File("$modDir/$moduleName/$defaultConfigFilename").exists()
if (defaultConfigExists) {
try { try {
val defaultConfig = JsonFetcher("$modDir/$moduleName/$defaultConfigFilename") val defaultConfig = if (defaultConfigHandle != null)
JsonFetcher.invoke(defaultConfigHandle)
else
JsonFetcher("$modDir/$moduleName/$defaultConfigFilename")
// read config and store it to the game // read config and store it to the game
var entry: JsonValue? = defaultConfig.child var entry: JsonValue? = defaultConfig.child
@@ -208,16 +233,29 @@ object ModMgr {
val jar = modMetadata.getProperty("jar") val jar = modMetadata.getProperty("jar")
val jarHash = modMetadata.getProperty("jarhash").uppercase() val jarHash = modMetadata.getProperty("jarhash").uppercase()
val dependency = modMetadata.getProperty("dependency").split(Regex(""";[ ]*""")).filter { it.isNotEmpty() }.toTypedArray() val dependency = modMetadata.getProperty("dependency").split(Regex(""";[ ]*""")).filter { it.isNotEmpty() }.toTypedArray()
val isDir = FileSystems.getDefault().getPath("$modDir/$moduleName").toFile().isDirectory val isDir = if (isInternal && AssetCache.isDistribution)
true // internal mods in archive are always "directories"
else
FileSystems.getDefault().getPath("$modDir/$moduleName").toFile().isDirectory
val configPlan = ArrayList<String>() val configPlan = ArrayList<String>()
File("$modDir/$moduleName/configplan.csv").let { val configPlanHandle = if (isInternal && AssetCache.isDistribution)
if (it.exists() && it.isFile) { AssetCache.getFileHandle("mods/$moduleName/configplan.csv")
configPlan.addAll(it.readLines(Common.CHARSET).filter { it.isNotBlank() }) else
null
if (configPlanHandle != null) {
if (configPlanHandle.exists()) {
configPlan.addAll(configPlanHandle.readString("UTF-8").lines().filter { it.isNotBlank() })
}
} else {
File("$modDir/$moduleName/configplan.csv").let {
if (it.exists() && it.isFile) {
configPlan.addAll(it.readLines(Common.CHARSET).filter { it.isNotBlank() })
}
} }
} }
module = ModuleMetadata(index, isDir, getGdxFile("$modDir/$moduleName/icon.png"), properName, description, descTranslations, author, packageName, entryPoint, releaseDate, version, jar, dependency, isInternal, configPlan) module = ModuleMetadata(index, isDir, getGdxFileLocal("$modDir/$moduleName/icon.png"), properName, description, descTranslations, author, packageName, entryPoint, releaseDate, version, jar, dependency, isInternal, configPlan)
val versionNumeral = version.split('.') val versionNumeral = version.split('.')
val versionNumber = versionNumeral.toVersionNumber() val versionNumber = versionNumeral.toVersionNumber()
@@ -447,24 +485,36 @@ object ModMgr {
/** Returning files are read-only */ /** Returning files are read-only */
fun getGdxFile(module: String, path: String): FileHandle { fun getGdxFile(module: String, path: String): FileHandle {
checkExistence(module) checkExistence(module)
return if (moduleInfo[module]!!.isInternal) return if (moduleInfo[module]!!.isInternal) {
Gdx.files.internal("$modDirInternal/$module/$path") if (AssetCache.isDistribution)
ClustfileHandle(AssetCache.getClustfile("mods/$module/$path"))
else
Gdx.files.internal("$modDirInternal/$module/$path")
}
else else
Gdx.files.absolute("$modDirExternal/$module/$path") Gdx.files.absolute("$modDirExternal/$module/$path")
} }
fun getFile(module: String, path: String): File { // getGdxFile is preferred due to asset archiving
/*fun getFile(module: String, path: String): File {
checkExistence(module) checkExistence(module)
return if (moduleInfo[module]!!.isInternal) return if (moduleInfo[module]!!.isInternal) {
FileSystems.getDefault().getPath("$modDirInternal/$module/$path").toFile() if (AssetCache.isDistribution)
throw UnsupportedOperationException("Use getGdxFile() for internal mod files in distribution mode (module=$module, path=$path)")
else
FileSystems.getDefault().getPath("$modDirInternal/$module/$path").toFile()
}
else else
FileSystems.getDefault().getPath("$modDirExternal/$module/$path").toFile() FileSystems.getDefault().getPath("$modDirExternal/$module/$path").toFile()
} }*/
fun hasFile(module: String, path: String): Boolean { fun hasFile(module: String, path: String): Boolean {
if (!moduleInfo.containsKey(module)) return false if (!moduleInfo.containsKey(module)) return false
return getFile(module, path).exists() return getGdxFile(module, path).exists()
} }
fun getFiles(module: String, path: String): Array<File> { // getGdxFile is preferred due to asset archiving
/*fun getFiles(module: String, path: String): Array<File> {
checkExistence(module) checkExistence(module)
if (moduleInfo[module]!!.isInternal && AssetCache.isDistribution)
throw UnsupportedOperationException("Use getGdxFiles() for internal mod files in distribution mode (module=$module, path=$path)")
val dir = getFile(module, path) val dir = getFile(module, path)
if (!dir.isDirectory) { if (!dir.isDirectory) {
throw FileNotFoundException("The path is not a directory") throw FileNotFoundException("The path is not a directory")
@@ -472,7 +522,7 @@ object ModMgr {
else { else {
return dir.listFiles() return dir.listFiles()
} }
} }*/
fun getGdxFiles(module: String, path: String): Array<FileHandle> { fun getGdxFiles(module: String, path: String): Array<FileHandle> {
checkExistence(module) checkExistence(module)
val dir = getGdxFile(module, path) val dir = getGdxFile(module, path)
@@ -487,20 +537,21 @@ object ModMgr {
/** Get a common file (literal file or directory) from all the installed mods. Files are guaranteed to exist. If a mod does not /** Get a common file (literal file or directory) from all the installed mods. Files are guaranteed to exist. If a mod does not
* contain the file, the mod will be skipped. * contain the file, the mod will be skipped.
* *
* @return List of pairs<modname, file> * @return List of pairs<modname, filehandle>
*/ */
fun getFilesFromEveryMod(path: String): List<Pair<String, File>> { // getGdxFile is preferred due to asset archiving
/*fun getFilesFromEveryMod(path: String): List<Pair<String, FileHandle>> {
val path = path.sanitisePath() val path = path.sanitisePath()
val moduleNames = moduleInfo.keys.toList() val moduleNames = moduleInfo.keys.toList()
val filesList = ArrayList<Pair<String, File>>() val filesList = ArrayList<Pair<String, FileHandle>>()
moduleNames.forEach { moduleNames.forEach {
val file = getFile(it, path) val file = getGdxFile(it, path)
if (file.exists()) filesList.add(it to file) if (file.exists()) filesList.add(it to file)
} }
return filesList.toList() return filesList.toList()
} }*/
/** Get a common file (literal file or directory) from all the installed mods. Files are guaranteed to exist. If a mod does not /** Get a common file (literal file or directory) from all the installed mods. Files are guaranteed to exist. If a mod does not
* contain the file, the mod will be skipped. * contain the file, the mod will be skipped.
@@ -739,7 +790,7 @@ object ModMgr {
const val langPath = "locales/" const val langPath = "locales/"
@JvmStatic operator fun invoke(module: String) { @JvmStatic operator fun invoke(module: String) {
Lang.load(getFile(module, langPath)) Lang.load(getGdxFile(module, langPath))
} }
} }
@@ -747,25 +798,25 @@ object ModMgr {
const val keebPath = "keylayout/" const val keebPath = "keylayout/"
@JvmStatic operator fun invoke(module: String) { @JvmStatic operator fun invoke(module: String) {
val FILE = getFile(module, keebPath) val DIR = getGdxFile(module, keebPath)
FILE.listFiles { file, s -> s.endsWith(".${IME.KEYLAYOUT_EXTENSION}") }.sortedBy { it.name }.forEach { DIR.list().filter { it.extension().equals(IME.KEYLAYOUT_EXTENSION, ignoreCase = true) }.sortedBy { it.name() }.forEach {
printdbg(this, "Registering Low layer ${it.nameWithoutExtension.lowercase()}") printdbg(this, "Registering Low layer ${it.nameWithoutExtension().lowercase()}")
IME.registerLowLayer(it.nameWithoutExtension.lowercase(), IME.parseKeylayoutFile(it)) IME.registerLowLayer(it.nameWithoutExtension().lowercase(), IME.parseKeylayoutFile(it))
} }
FILE.listFiles { file, s -> s.endsWith(".${IME.IME_EXTENSION}") }.sortedBy { it.name }.forEach { DIR.list().filter { it.extension().equals(IME.IME_EXTENSION, ignoreCase = true) }.sortedBy { it.name() }.forEach {
printdbg(this, "Registering High layer ${it.nameWithoutExtension.lowercase()}") printdbg(this, "Registering High layer ${it.nameWithoutExtension().lowercase()}")
IME.registerHighLayer(it.nameWithoutExtension.lowercase(), IME.parseImeFile(it)) IME.registerHighLayer(it.nameWithoutExtension().lowercase(), IME.parseImeFile(it))
} }
val iconFile = getFile(module, keebPath + "icons.tga").let { val iconFile = getGdxFile(module, keebPath + "icons.tga").let {
if (it.exists()) it else getFile(module, keebPath + "icons.png") if (it.exists()) it else getGdxFile(module, keebPath + "icons.png")
} }
if (iconFile.exists()) { if (iconFile.exists()) {
val iconSheet = TextureRegionPack(iconFile.path, 20, 20) val iconSheet = TextureRegionPack(iconFile, 20, 20)
val iconPixmap = Pixmap(Gdx.files.absolute(iconFile.path)) val iconPixmap = Pixmap(iconFile)
for (k in 0 until iconPixmap.height step 20) { for (k in 0 until iconPixmap.height step 20) {
val langCode = StringBuilder() val langCode = StringBuilder()
for (c in 0 until 20) { for (c in 0 until 20) {
@@ -855,7 +906,7 @@ object ModMgr {
} }
@JvmStatic operator fun invoke(module: String) { @JvmStatic operator fun invoke(module: String) {
getFiles(module, weatherPath).filter { it.isFile && it.name.lowercase().endsWith(".json") }.forEach { getGdxFile(module, weatherPath).list().filter { !it.isDirectory && it.name().lowercase().endsWith(".json") }.forEach {
Terrarum.weatherCodex.readFromJson(module, it) Terrarum.weatherCodex.readFromJson(module, it)
} }
} }
@@ -878,32 +929,21 @@ object ModMgr {
} }
@JvmStatic operator fun invoke(module: String) { @JvmStatic operator fun invoke(module: String) {
val targetModNames = getFiles(module, retexturesPath).filter { it.isDirectory } val targetModNames = getGdxFile(module, retexturesPath).list().filter { it.isDirectory }
targetModNames.forEach { baseTargetModDir -> targetModNames.forEach { baseTargetModDir ->
// modules/<module>/retextures/basegame
// printdbg(this, "baseTargetModDir = $baseTargetModDir")
retexables.forEach { category -> retexables.forEach { category ->
val dir = File(baseTargetModDir, category) val dir = baseTargetModDir.child(category)
// modules/<module>/retextures/basegame/blocks
// printdbg(this, "cats: ${dir.path}")
if (dir.isDirectory && dir.exists()) { if (dir.isDirectory && dir.exists()) {
dir.listFiles { it: File -> dir.list().filter { it.name().contains('-') }.forEach {
it.name.contains('-') val tokens = it.name().split('-')
}?.forEach {
// <other modname>-<hopefully a number>.tga or .png
val tokens = it.name.split('-')
if (tokens.size > 1) { if (tokens.size > 1) {
val modname = tokens[0] val modname = tokens[0]
val filename = tokens.tail().joinToString("-") val filename = tokens.tail().joinToString("-")
altFilePaths["$modDirInternal/$modname/$category/$filename"] = getGdxFile(module, "$retexturesPath${baseTargetModDir.name}/$category/${it.name}") altFilePaths["$modDirInternal/$modname/$category/$filename"] = getGdxFile(module, "$retexturesPath${baseTargetModDir.name()}/$category/${it.name()}")
} }
} }
} }
// retexableCallbacks[category]?.invoke()
} }
} }
@@ -917,12 +957,12 @@ object ModMgr {
const val smeltingPath = "smelting/" const val smeltingPath = "smelting/"
@JvmStatic operator fun invoke(module: String) { @JvmStatic operator fun invoke(module: String) {
getFile(module, recipePath).listFiles { it: File -> it.name.lowercase().endsWith(".json") }?.forEach { jsonFile -> getGdxFile(module, recipePath).list().filter { !it.isDirectory && it.name().lowercase().endsWith(".json") }.forEach { jsonHandle ->
Terrarum.craftingCodex.addFromJson(JsonFetcher(jsonFile), module, jsonFile.name) Terrarum.craftingCodex.addFromJson(JsonFetcher.invoke(jsonHandle), module, jsonHandle.name())
} }
getFile(module, smeltingPath).listFiles { it: File -> it.name.lowercase().endsWith(".json") }?.forEach { jsonFile -> getGdxFile(module, smeltingPath).list().filter { !it.isDirectory && it.name().lowercase().endsWith(".json") }.forEach { jsonHandle ->
Terrarum.craftingCodex.addSmeltingFromJson(JsonFetcher(jsonFile), module, jsonFile.name) Terrarum.craftingCodex.addSmeltingFromJson(JsonFetcher.invoke(jsonHandle), module, jsonHandle.name())
} }
} }
} }

View File

@@ -1,13 +1,13 @@
package net.torvald.terrarum.audio package net.torvald.terrarum.audio
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.reflection.forceInvoke import net.torvald.reflection.forceInvoke
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.CommonResourcePool import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ModMgr import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.serialise.toUint import net.torvald.terrarum.serialise.toUint
import java.io.File
/** /**
* Created by minjaesong on 2024-01-24. * Created by minjaesong on 2024-01-24.
@@ -26,7 +26,7 @@ object AudioHelper {
return fft return fft
} }
else { else {
val ir = ModMgr.getFile(module, path) val ir = ModMgr.getGdxFile(module, path)
val fft = createIR(ir) val fft = createIR(ir)
CommonResourcePool.addToLoadingList(id) { fft } CommonResourcePool.addToLoadingList(id) { fft }
@@ -36,19 +36,20 @@ object AudioHelper {
} }
} }
private fun createIR(ir: File): Array<ComplexArray> { private fun createIR(ir: FileHandle): Array<ComplexArray> {
if (!ir.exists()) { if (!ir.exists()) {
throw IllegalArgumentException("Impulse Response file '${ir.path}' does not exist.") throw IllegalArgumentException("Impulse Response file '${ir.path()}' does not exist.")
} }
val sampleCount = (ir.length().toInt() / 8)//.coerceAtMost(65536) val irBytes = ir.readBytes()
val sampleCount = (irBytes.size / 8)//.coerceAtMost(65536)
val fftLen = FastMath.nextPowerOfTwo(sampleCount) val fftLen = FastMath.nextPowerOfTwo(sampleCount)
printdbg(this, "IR '${ir.path}' Sample Count = $sampleCount; FFT Length = $fftLen") printdbg(this, "IR '${ir.path()}' Sample Count = $sampleCount; FFT Length = $fftLen")
val conv = Array(2) { FloatArray(fftLen) } val conv = Array(2) { FloatArray(fftLen) }
ir.inputStream().let { java.io.ByteArrayInputStream(irBytes).let {
for (i in 0 until sampleCount) { for (i in 0 until sampleCount) {
val f1 = Float.fromBits(it.read().and(255) or val f1 = Float.fromBits(it.read().and(255) or
it.read().and(255).shl(8) or it.read().and(255).shl(8) or
@@ -79,7 +80,7 @@ object AudioHelper {
return CommonResourcePool.getAs<Array<FloatArray>>(id) return CommonResourcePool.getAs<Array<FloatArray>>(id)
} }
else { else {
val file = ModMgr.getFile(module, path) val file = ModMgr.getGdxFile(module, path)
val samples = createAudioInSamples(file) val samples = createAudioInSamples(file)
CommonResourcePool.addToLoadingList(id) { samples } CommonResourcePool.addToLoadingList(id) { samples }
@@ -89,8 +90,8 @@ object AudioHelper {
} }
} }
private fun createAudioInSamples(static: File): Array<FloatArray> { private fun createAudioInSamples(static: FileHandle): Array<FloatArray> {
val music = Gdx.audio.newMusic(Gdx.files.absolute(static.absolutePath)) val music = Gdx.audio.newMusic(static)
val readbuf = ByteArray(AudioProcessBuf.MP3_CHUNK_SIZE * 4) val readbuf = ByteArray(AudioProcessBuf.MP3_CHUNK_SIZE * 4)
val OUTBUF_BLOCK_SIZE_IN_BYTES = (48000 * 60) * 2 * 2 val OUTBUF_BLOCK_SIZE_IN_BYTES = (48000 * 60) * 2 * 2
var outbuf = ByteArray(OUTBUF_BLOCK_SIZE_IN_BYTES) var outbuf = ByteArray(OUTBUF_BLOCK_SIZE_IN_BYTES)

View File

@@ -18,13 +18,15 @@ import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RAT
import net.torvald.terrarum.serialise.toUint import net.torvald.terrarum.serialise.toUint
import net.torvald.unsafe.UnsafeHelper import net.torvald.unsafe.UnsafeHelper
import net.torvald.unsafe.UnsafePtr import net.torvald.unsafe.UnsafePtr
import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import javax.sound.sampled.AudioSystem import javax.sound.sampled.AudioSystem
class MusicContainer( class MusicContainer(
override val name: String, override val name: String,
val file: File, val file: File?,
val fileHandle: FileHandle?,
val looping: Boolean = false, val looping: Boolean = false,
val toRAM: Boolean = false, val toRAM: Boolean = false,
val samplingRateOverride: Float?, // this is FIXED sampling rate val samplingRateOverride: Float?, // this is FIXED sampling rate
@@ -34,6 +36,15 @@ class MusicContainer(
override var channels: Int override var channels: Int
val codec: String val codec: String
// File-based constructors (existing API)
constructor(
name: String,
file: File,
looping: Boolean = false,
toRAM: Boolean = false,
samplingRateOverride: Float?,
songFinishedHook: (AudioBank) -> Unit = {}
) : this(name, file, null, looping, toRAM, samplingRateOverride, songFinishedHook)
// make Java code shorter // make Java code shorter
constructor( constructor(
name: String, name: String,
@@ -41,27 +52,37 @@ class MusicContainer(
looping: Boolean = false, looping: Boolean = false,
toRAM: Boolean = false, toRAM: Boolean = false,
songFinishedHook: (AudioBank) -> Unit = {} songFinishedHook: (AudioBank) -> Unit = {}
) : this(name, file, looping, toRAM, null, songFinishedHook) ) : this(name, file, null, looping, toRAM, null, songFinishedHook)
// make Java code shorter // make Java code shorter
constructor( constructor(
name: String, name: String,
file: File, file: File,
looping: Boolean = false, looping: Boolean = false,
songFinishedHook: (AudioBank) -> Unit = {} songFinishedHook: (AudioBank) -> Unit = {}
) : this(name, file, looping, false, null, songFinishedHook) ) : this(name, file, null, looping, false, null, songFinishedHook)
// make Java code shorter // make Java code shorter
constructor( constructor(
name: String, name: String,
file: File, file: File,
songFinishedHook: (AudioBank) -> Unit = {} songFinishedHook: (AudioBank) -> Unit = {}
) : this(name, file, false, false, null, songFinishedHook) ) : this(name, file, null, false, false, null, songFinishedHook)
// FileHandle-based constructor (for TEVD archive support)
constructor(
name: String,
fileHandle: FileHandle,
looping: Boolean = false,
toRAM: Boolean = false,
samplingRateOverride: Float? = null,
songFinishedHook: (AudioBank) -> Unit = {}
) : this(name, null, fileHandle, looping, toRAM, samplingRateOverride, songFinishedHook)
var samplesReadCount = 0L; internal set var samplesReadCount = 0L; internal set
override var totalSizeInSamples: Long override var totalSizeInSamples: Long
private val totalSizeInBytes: Long private val totalSizeInBytes: Long
private val gdxMusic: Music = Gdx.audio.newMusic(FileHandle(file)) private val gdxMusic: Music = if (file != null) Gdx.audio.newMusic(FileHandle(file)) else Gdx.audio.newMusic(fileHandle!!)
private var soundBuf: UnsafePtr? = null; private set private var soundBuf: UnsafePtr? = null; private set
@@ -94,7 +115,10 @@ class MusicContainer(
rate.toFloat() rate.toFloat()
} }
is Mp3.Music -> { is Mp3.Music -> {
val tempMusic = Gdx.audio.newMusic(Gdx.files.absolute(file.absolutePath)) val tempMusic = if (file == null)
Gdx.audio.newMusic(fileHandle)
else
Gdx.audio.newMusic(Gdx.files.absolute(file.absolutePath))
val bitstream = tempMusic.extortField<Bitstream>("bitstream")!! val bitstream = tempMusic.extortField<Bitstream>("bitstream")!!
val header = bitstream.readFrame() val header = bitstream.readFrame()
val rate = header.sampleRate val rate = header.sampleRate
@@ -118,11 +142,22 @@ class MusicContainer(
if (it.last() == "Music") it.dropLast(1).last() else it.last() if (it.last() == "Music") it.dropLast(1).last() else it.last()
} }
totalSizeInSamples = when (gdxMusic) { totalSizeInSamples = if (file != null) {
is Wav.Music -> getWavFileSampleCount(file) when (gdxMusic) {
is Ogg.Music -> getOggFileSampleCount(file) is Wav.Music -> getWavFileSampleCount(file)
is Mp3.Music -> getMp3FileSampleCount(file) is Ogg.Music -> getOggFileSampleCount(file)
else -> Long.MAX_VALUE is Mp3.Music -> getMp3FileSampleCount(file)
else -> Long.MAX_VALUE
}
} else if (fileHandle != null) {
when (gdxMusic) {
is Wav.Music -> getWavFileSampleCountFromHandle(fileHandle)
is Ogg.Music -> getOggFileSampleCountFromHandle(fileHandle)
is Mp3.Music -> getMp3FileSampleCountFromHandle(fileHandle)
else -> Long.MAX_VALUE
}
} else {
Long.MAX_VALUE
} }
totalSizeInBytes = totalSizeInSamples * 2 * channels totalSizeInBytes = totalSizeInSamples * 2 * channels
@@ -329,10 +364,65 @@ class MusicContainer(
} }
} }
override fun toString() = if (name.isEmpty()) file.nameWithoutExtension else name private fun getWavFileSampleCountFromHandle(fh: FileHandle): Long {
return try {
val ais = AudioSystem.getAudioInputStream(BufferedInputStream(fh.read()))
val r = ais.frameLength
ais.close()
r
}
catch (_: Throwable) {
Long.MAX_VALUE
}
}
override fun equals(other: Any?) = this.file.path == (other as MusicContainer).file.path private fun getOggFileSampleCountFromHandle(fh: FileHandle): Long {
fun equalInstance(other: Any?) = this.file.path == (other as MusicContainer).file.path && this.hash == (other as MusicContainer).hash return try {
// VorbisFile requires a file path; use a temp file
val tempFile = java.io.File.createTempFile("terrarum_ogg_", ".ogg")
tempFile.deleteOnExit()
tempFile.writeBytes(fh.readBytes())
val vorbisFile = VorbisFile(tempFile.absolutePath)
val r = vorbisFile.pcm_total(0)
tempFile.delete()
r
}
catch (_: Throwable) {
Long.MAX_VALUE
}
}
private fun getMp3FileSampleCountFromHandle(fh: FileHandle): Long {
return try {
val input = BufferedInputStream(fh.read())
val bs = Bitstream(input)
var header = bs.readFrame()
val rate = header.frequency()
var totalSamples = 0L
while (header != null) {
totalSamples += (header.ms_per_frame() * rate / 1000).toLong()
bs.closeFrame()
header = bs.readFrame()
}
bs.close()
input.close()
totalSamples
}
catch (_: Throwable) {
Long.MAX_VALUE
}
}
private val identPath: String get() = file?.path ?: fileHandle?.path() ?: name
override fun toString() = if (name.isEmpty()) (file?.nameWithoutExtension ?: fileHandle?.nameWithoutExtension() ?: "") else name
override fun equals(other: Any?) = this.identPath == (other as MusicContainer).identPath
fun equalInstance(other: Any?) = this.identPath == (other as MusicContainer).identPath && this.hash == (other as MusicContainer).hash
override fun dispose() { override fun dispose() {
gdxMusic.dispose() gdxMusic.dispose()
@@ -340,7 +430,10 @@ class MusicContainer(
} }
override fun makeCopy(): AudioBank { override fun makeCopy(): AudioBank {
val new = MusicContainer(name, file, looping, false, samplingRateOverride, songFinishedHook) val new = if (file != null)
MusicContainer(name, file, looping, false, samplingRateOverride, songFinishedHook)
else
MusicContainer(name, fileHandle!!, looping, false, samplingRateOverride, songFinishedHook)
synchronized(this) { synchronized(this) {
if (this.toRAM) { if (this.toRAM) {

View File

@@ -38,13 +38,13 @@ object CommandDict {
((listOf("$" to "net.torvald.terrarum")) + ModMgr.loadOrder.reversed().map { it to ModMgr.moduleInfo[it]?.packageName }).forEach { (modName, packageRoot) -> ((listOf("$" to "net.torvald.terrarum")) + ModMgr.loadOrder.reversed().map { it to ModMgr.moduleInfo[it]?.packageName }).forEach { (modName, packageRoot) ->
if (modName == "$" || modName != "$" && ModMgr.hasFile(modName, "commands.csv")) { if (modName == "$" || modName != "$" && ModMgr.hasFile(modName, "commands.csv")) {
val commandsList = if (modName == "$") engineCommandList else ModMgr.getFile(modName, "commands.csv").readLines() val commandsList = if (modName == "$") engineCommandList else ModMgr.getGdxFile(modName, "commands.csv").readString("UTF-8").lines()
val packageConsole = "$packageRoot.console" val packageConsole = "$packageRoot.console"
printdbg(this, "Loading console commands from '${packageConsole}'") printdbg(this, "Loading console commands from '${packageConsole}'")
// printdbg(this, commandsList.joinToString()) // printdbg(this, commandsList.joinToString())
commandsList.forEach { commandName -> commandsList.filter { it.isNotBlank() }.forEach { commandName ->
val canonicalName = "$packageConsole.$commandName" val canonicalName = "$packageConsole.$commandName"
val it = ModMgr.moduleClassloader[modName].let { val it = ModMgr.moduleClassloader[modName].let {
if (it != null) if (it != null)

View File

@@ -15,7 +15,7 @@ object FactionFactory {
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun create(module: String, path: String): Faction { fun create(module: String, path: String): Faction {
val jsonObj = JsonFetcher(ModMgr.getFile(module, path)) val jsonObj = JsonFetcher(ModMgr.getGdxFile(module, path))
val factionObj = Faction(jsonObj.getString("factionname")) val factionObj = Faction(jsonObj.getString("factionname"))
jsonObj.get("factionamicable").asStringArray().forEach { factionObj.addFactionAmicable(it) } jsonObj.get("factionamicable").asStringArray().forEach { factionObj.addFactionAmicable(it) }

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.gamecontroller package net.torvald.terrarum.gamecontroller
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.App import net.torvald.terrarum.App
@@ -173,8 +174,13 @@ object IME {
else -> throw IllegalArgumentException("Unknown candidates mode: $this") else -> throw IllegalArgumentException("Unknown candidates mode: $this")
} }
fun parseKeylayoutFile(file: File): TerrarumKeyLayout { fun parseKeylayoutFile(fileHandle: FileHandle): TerrarumKeyLayout =
val src = file.readText(Charsets.UTF_8) parseKeylayoutFromString(fileHandle.readString("UTF-8"))
fun parseKeylayoutFile(file: File): TerrarumKeyLayout =
parseKeylayoutFromString(file.readText(Charsets.UTF_8))
private fun parseKeylayoutFromString(src: String): TerrarumKeyLayout {
val jsval = context.eval("js", "'use strict';Object.freeze($src)") val jsval = context.eval("js", "'use strict';Object.freeze($src)")
val name = jsval.getMember("n").asString() val name = jsval.getMember("n").asString()
val capsmode = jsval.getMember("capslock").asString().toCapsMode() val capsmode = jsval.getMember("capslock").asString().toCapsMode()
@@ -201,8 +207,6 @@ object IME {
} }
} }
// println("[IME] Test Keymap print for $name:"); for (keycode in 0 until 256) { print("$keycode:\t"); println(out[keycode].joinToString("\t")) }
return TerrarumKeyLayout(name, capsmode, out, physicalLayout) return TerrarumKeyLayout(name, capsmode, out, physicalLayout)
} }
@@ -215,8 +219,13 @@ object IME {
else -> throw IllegalArgumentException("Unknown operation mode: $this") else -> throw IllegalArgumentException("Unknown operation mode: $this")
} }
fun parseImeFile(file: File): TerrarumIME { fun parseImeFile(fileHandle: FileHandle): TerrarumIME =
val code = file.readText(Charsets.UTF_8) parseImeFromString(fileHandle.readString("UTF-8"))
fun parseImeFile(file: File): TerrarumIME =
parseImeFromString(file.readText(Charsets.UTF_8))
private fun parseImeFromString(code: String): TerrarumIME {
val jsval = context.eval("js", "\"use strict\";(function(){$code})()") val jsval = context.eval("js", "\"use strict\";(function(){$code})()")
val name = jsval.getMember("n").asString() val name = jsval.getMember("n").asString()
val candidatesCount = jsval.getMember("v").asString().toViewCount() val candidatesCount = jsval.getMember("v").asString().toViewCount()

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.langpack package net.torvald.terrarum.langpack
import com.badlogic.gdx.files.FileHandle
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.tail import net.torvald.terrarum.tail
@@ -82,47 +83,62 @@ object Lang {
} }
} }
fun load(localesHandle: FileHandle) {
printdbg(this, "Loading languages from ${localesHandle.path()}")
localesHandle.list().filter { it.isDirectory }.forEach { languageList.add(it.name()) }
// temporary filter
languageList.remove("jaJPysi")
for (lang in languageList) {
printdbg(this, "Loading langpack from ${localesHandle.path()}/$lang/")
val langFiles = localesHandle.child(lang).list()
langFiles.forEach {
if (!it.name().startsWith("Polyglot") && it.name().endsWith(".json")) {
processRegularLangfile(it, lang)
}
else if (it.name().startsWith("Polyglot") && it.name().endsWith(".json")) {
processPolyglotLangFile(it, lang)
}
}
}
}
private fun processRegularLangfile(file: File, lang: String) { private fun processRegularLangfile(file: File, lang: String) {
val json = JsonFetcher(file) val json = JsonFetcher(file)
/*
* Terrarum langpack JSON structure is:
*
* (root object)
* "<<STRING ID>>" = "<<LOCALISED TEXT>>"
*/
//println(json.entrySet())
JsonFetcher.forEachSiblings(json) { key, value -> JsonFetcher.forEachSiblings(json) { key, value ->
langpack.put("${key}_$lang", value.asString().trim()) langpack.put("${key}_$lang", value.asString().trim())
} }
}
private fun processRegularLangfile(fileHandle: FileHandle, lang: String) {
val json = JsonFetcher(fileHandle)
JsonFetcher.forEachSiblings(json) { key, value ->
langpack.put("${key}_$lang", value.asString().trim())
}
} }
private fun processPolyglotLangFile(file: File, lang: String) { private fun processPolyglotLangFile(file: File, lang: String) {
val json = JsonFetcher(file) val json = JsonFetcher(file)
/*
* Polyglot JSON structure is:
*
* (root object)
* "resources": object
* "polyglot": object
* (polyglot meta)
* "data": array
* [0]: object
* n = "CONTEXT_CHARACTER_CLASS"
* s = "Class"
* [1]: object
* n = "CONTEXT_CHARACTER_DELETE"
* s = "Delecte Character"
* (the array continues)
*
*/
JsonFetcher.forEachSiblings(json.get("resources").get("data")) { _, entry -> JsonFetcher.forEachSiblings(json.get("resources").get("data")) { _, entry ->
langpack.put( langpack.put(
"${entry.getString("n")}_$lang", "${entry.getString("n")}_$lang",
entry.getString("s").trim() entry.getString("s").trim()
) )
} }
}
private fun processPolyglotLangFile(fileHandle: FileHandle, lang: String) {
val json = JsonFetcher(fileHandle)
JsonFetcher.forEachSiblings(json.get("resources").get("data")) { _, entry ->
langpack.put(
"${entry.getString("n")}_$lang",
entry.getString("s").trim()
)
}
} }
private val bindOp = ">>=" private val bindOp = ">>="

View File

@@ -161,10 +161,10 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
try { try {
val file = ModMgr.getFile("basegame", "demoworld") val fileHandle = ModMgr.getGdxFile("basegame", "demoworld")
val reader = java.io.FileReader(file) val reader = fileHandle.reader("UTF-8")
//ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader) //ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader)
val world = ReadSimpleWorld(reader, file) val world = ReadSimpleWorld(reader, fileHandle.file())
demoWorld = world demoWorld = world
demoWorld.worldTime.timeDelta = 30 demoWorld.worldTime.timeDelta = 30
printdbg(this, "Demo world loaded") printdbg(this, "Demo world loaded")

View File

@@ -46,7 +46,7 @@ object InstrumentLoader {
CommonResourcePool.getAs<Pair<FloatArray, FloatArray>>("${baseResourceName}_$it") CommonResourcePool.getAs<Pair<FloatArray, FloatArray>>("${baseResourceName}_$it")
} }
val masterFile = MusicContainer("${idBase}_${initialNote}", ModMgr.getFile(module, path)) val masterFile = MusicContainer("${idBase}_${initialNote}", ModMgr.getGdxFile(module, path))
val masterSamplesL = FloatArray(masterFile.totalSizeInSamples.toInt()) val masterSamplesL = FloatArray(masterFile.totalSizeInSamples.toInt())
val masterSamplesR = FloatArray(masterFile.totalSizeInSamples.toInt()) val masterSamplesR = FloatArray(masterFile.totalSizeInSamples.toInt())
masterFile.readSamples(masterSamplesL, masterSamplesR) masterFile.readSamples(masterSamplesL, masterSamplesR)

View File

@@ -23,7 +23,7 @@ open class ActorLobbed(throwPitch: Float) : ActorWithBody() {
@Transient private val pitch = throwPitch.coerceIn(0.5f, 2f) @Transient private val pitch = throwPitch.coerceIn(0.5f, 2f)
@Transient private val whooshSound = MusicContainer( @Transient private val whooshSound = MusicContainer(
"throw_low_short", ModMgr.getFile("basegame", "audio/effects/throwing/throw_low_short.wav"), "throw_low_short", ModMgr.getGdxFile("basegame", "audio/effects/throwing/throw_low_short.wav"),
toRAM = true, toRAM = true,
samplingRateOverride = 48000f * pitch samplingRateOverride = 48000f * pitch
) )
@@ -63,17 +63,17 @@ open class ActorPrimedBomb(
private var explosionCalled = false private var explosionCalled = false
@Transient private val boomSound = MusicContainer( @Transient private val boomSound = MusicContainer(
"boom", ModMgr.getFile("basegame", "audio/effects/explosion/bang_bomb.wav"), toRAM = true "boom", ModMgr.getGdxFile("basegame", "audio/effects/explosion/bang_bomb.wav"), toRAM = true
) { ) {
this.flagDespawn() this.flagDespawn()
} }
@Transient private val fuseSound = MusicContainer( @Transient private val fuseSound = MusicContainer(
"fuse", ModMgr.getFile("basegame", "audio/effects/explosion/fuse.wav"), toRAM = true "fuse", ModMgr.getGdxFile("basegame", "audio/effects/explosion/fuse.wav"), toRAM = true
) { ) {
this.flagDespawn() this.flagDespawn()
} }
@Transient private val fuseSoundCont = MusicContainer( @Transient private val fuseSoundCont = MusicContainer(
"fuse_continue", ModMgr.getFile("basegame", "audio/effects/explosion/fuse_continue.wav"), toRAM = true "fuse_continue", ModMgr.getGdxFile("basegame", "audio/effects/explosion/fuse_continue.wav"), toRAM = true
) { ) {
this.flagDespawn() this.flagDespawn()
} }

View File

@@ -171,7 +171,7 @@ class FixtureAlloyingFurnace : FixtureBase {
} }
@Transient val static = MusicContainer("bonfire", ModMgr.getFile("basegame", "audio/effects/static/bonfire.ogg"), true) @Transient val static = MusicContainer("bonfire", ModMgr.getGdxFile("basegame", "audio/effects/static/bonfire.ogg"), true)
@Transient val light = Cvec(0.5f, 0.18f, 0f, 0f) @Transient val light = Cvec(0.5f, 0.18f, 0f, 0f)
@Transient override var lightBoxList = arrayListOf(Lightbox(Hitbox(0.0, 0.0, TILE_SIZED * 2, TILE_SIZED * 2), light)) @Transient override var lightBoxList = arrayListOf(Lightbox(Hitbox(0.0, 0.0, TILE_SIZED * 2, TILE_SIZED * 2), light))

View File

@@ -57,7 +57,7 @@ class FixtureFurnaceAndAnvil : FixtureBase, CraftingStation {
} }
} }
@Transient val static = MusicContainer("bonfire", ModMgr.getFile("basegame", "audio/effects/static/bonfire.ogg"), true) @Transient val static = MusicContainer("bonfire", ModMgr.getGdxFile("basegame", "audio/effects/static/bonfire.ogg"), true)
@Transient override var lightBoxList = arrayListOf(Lightbox(Hitbox(0.0, 0.0, TerrarumAppConfiguration.TILE_SIZED * 2, TerrarumAppConfiguration.TILE_SIZED * 2), Cvec(0.5f, 0.18f, 0f, 0f))) @Transient override var lightBoxList = arrayListOf(Lightbox(Hitbox(0.0, 0.0, TerrarumAppConfiguration.TILE_SIZED * 2, TerrarumAppConfiguration.TILE_SIZED * 2), Cvec(0.5f, 0.18f, 0f, 0f)))

View File

@@ -156,7 +156,7 @@ class FixtureSmelterBasic : FixtureBase {
this.mainUI = UISmelterBasic(this) this.mainUI = UISmelterBasic(this)
} }
@Transient val static = MusicContainer("bonfire", ModMgr.getFile("basegame", "audio/effects/static/bonfire.ogg"), true) @Transient val static = MusicContainer("bonfire", ModMgr.getGdxFile("basegame", "audio/effects/static/bonfire.ogg"), true)
@Transient val light = Cvec(0.5f, 0.18f, 0f, 0f) @Transient val light = Cvec(0.5f, 0.18f, 0f, 0f)
@Transient override var lightBoxList = arrayListOf(Lightbox(Hitbox(0.0, 2*TILE_SIZED, TILE_SIZED * 2, TILE_SIZED * 2), light)) @Transient override var lightBoxList = arrayListOf(Lightbox(Hitbox(0.0, 2*TILE_SIZED, TILE_SIZED * 2, TILE_SIZED * 2), light))

View File

@@ -98,7 +98,7 @@ class FixtureTypewriter : FixtureBase {
internal class TestLeafletPrimaryUseHandler : FileRefItemPrimaryUseHandler { internal class TestLeafletPrimaryUseHandler : FileRefItemPrimaryUseHandler {
override fun use(item: ItemFileRef): Long { override fun use(item: ItemFileRef): Long {
println(item.getAsFile().readText(Common.CHARSET)) println(item.getAsGdxFile().readString(Common.CHARSET.displayName()))
return 0L return 0L
} }
} }

View File

@@ -24,7 +24,7 @@ object InjectCreatureRaw {
* @param jsonFileName with extension * @param jsonFileName with extension
*/ */
operator fun invoke(actorValueRef: ActorValue, module: String, jsonFileName: String) { operator fun invoke(actorValueRef: ActorValue, module: String, jsonFileName: String) {
val jsonObj = JsonFetcher(ModMgr.getFile(module, "creatures/$jsonFileName")) val jsonObj = JsonFetcher(ModMgr.getGdxFile(module, "creatures/$jsonFileName"))
JsonFetcher.forEachSiblings(jsonObj) { key, value -> if (!key.startsWith("_")) { JsonFetcher.forEachSiblings(jsonObj) { key, value -> if (!key.startsWith("_")) {

View File

@@ -7,6 +7,7 @@ import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.ItemID
import com.badlogic.gdx.files.FileHandle
import java.io.File import java.io.File
import java.util.UUID import java.util.UUID
@@ -42,7 +43,7 @@ open class ItemFileRef(originalID: ItemID) : GameItem(originalID) {
*/ */
open var refIsShared: Boolean = false open var refIsShared: Boolean = false
@Transient open lateinit var ref: File @Transient open lateinit var ref: FileHandle
/** /**
* Application-defined. * Application-defined.
@@ -101,10 +102,10 @@ open class ItemFileRef(originalID: ItemID) : GameItem(originalID) {
else else
ModMgr.getGdxFile(refModuleName, refPath) ModMgr.getGdxFile(refModuleName, refPath)
fun getAsFile() = if (refIsShared) /*fun getAsFile() = if (refIsShared)
File(App.saveSharedDir + "/$refPath") File(App.saveSharedDir + "/$refPath")
else else
ModMgr.getFile(refModuleName, refPath) ModMgr.getFile(refModuleName, refPath)*/
@Transient private var classCache: FileRefItemPrimaryUseHandler? = null @Transient private var classCache: FileRefItemPrimaryUseHandler? = null

View File

@@ -22,7 +22,7 @@ data class MusicDiscMetadata(val title: String, val author: String, val album: S
object MusicDiscHelper { object MusicDiscHelper {
fun getMetadata(musicFile: FileHandle): MusicDiscMetadata { fun getMetadata(musicFile: FileHandle): MusicDiscMetadata {
val musicdbFile = musicFile.sibling("_musicdb.json") val musicdbFile = musicFile.sibling("_musicdb.json")
val musicdb = JsonFetcher.invoke(musicdbFile.file()) val musicdb = JsonFetcher.invoke(musicdbFile)
val propForThisFile = musicdb.get(musicFile.name()) val propForThisFile = musicdb.get(musicFile.name())
val artist = propForThisFile.get("artist").asString() val artist = propForThisFile.get("artist").asString()
@@ -37,7 +37,7 @@ open class MusicDiscPrototype(originalID: ItemID, module: String, path: String)
override var refPath = path override var refPath = path
override var refModuleName = module override var refModuleName = module
override val canBeDynamic = false override val canBeDynamic = false
@Transient override var ref = ModMgr.getFile(refModuleName, refPath) @Transient override var ref = ModMgr.getGdxFile(refModuleName, refPath)
override var mediumIdentifier = "music_disc" override var mediumIdentifier = "music_disc"
init { init {

View File

@@ -273,7 +273,7 @@ object PickaxeCore : TooltipListener() {
private val soundCue = MusicContainer( private val soundCue = MusicContainer(
"pickaxe_sound_cue", "pickaxe_sound_cue",
ModMgr.getFile("basegame", "audio/effects/accessibility/pickaxe_valuable.ogg"), ModMgr.getGdxFile("basegame", "audio/effects/accessibility/pickaxe_valuable.ogg"),
toRAM = false toRAM = false
).also { ).also {
App.disposables.add(it) App.disposables.add(it)

View File

@@ -30,7 +30,7 @@ class UIElemTest : ApplicationAdapter() {
private lateinit var ui: UICanvas private lateinit var ui: UICanvas
override fun create() { override fun create() {
App.fontGame = TerrarumSansBitmap(App.FONT_DIR, false, true, false, App.fontGame = TerrarumSansBitmap(false, true, false,
false, false,
256, false, 0.5f, false 256, false, 0.5f, false
) )

View File

@@ -40,7 +40,10 @@ object CSVFetcher {
return csvRecordList return csvRecordList
} }
fun readFromModule(module: String, path: String) = net.torvald.terrarum.utils.CSVFetcher.readFromFile(ModMgr.getGdxFile(module, path).path()) fun readFromModule(module: String, path: String): List<org.apache.commons.csv.CSVRecord> {
val content = ModMgr.getGdxFile(module, path).readString("UTF-8")
return readFromString(content)
}
fun readFromString(csv: String): List<org.apache.commons.csv.CSVRecord> { fun readFromString(csv: String): List<org.apache.commons.csv.CSVRecord> {
val preprocessed = preprocessCSV(csv) val preprocessed = preprocessCSV(csv)

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.utils package net.torvald.terrarum.utils
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.JsonReader import com.badlogic.gdx.utils.JsonReader
import com.badlogic.gdx.utils.JsonValue import com.badlogic.gdx.utils.JsonValue
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
@@ -36,6 +37,13 @@ object JsonFetcher {
return JsonReader().parse(jsonString.toString()) return JsonReader().parse(jsonString.toString())
} }
@Throws(java.io.IOException::class)
operator fun invoke(fileHandle: FileHandle): JsonValue {
val content = fileHandle.readString("UTF-8")
printdbg(this, "Reading JSON ${fileHandle.path()}")
return JsonReader().parse(content)
}
fun readFromJsonString(stringReader: Reader): JsonValue { fun readFromJsonString(stringReader: Reader): JsonValue {
return JsonReader().parse(stringReader.readText()) return JsonReader().parse(stringReader.readText())
} }

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.weather package net.torvald.terrarum.weather
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
@@ -40,24 +41,17 @@ class WeatherCodex : Disposable {
fun readFromJson(modname: String, file: File) = readFromJson(modname, file.path) fun readFromJson(modname: String, file: File) = readFromJson(modname, file.path)
fun readFromJson(modname: String, fileHandle: FileHandle) {
readFromJsonValue(modname, JsonFetcher.invoke(fileHandle))
}
private val pathToImage = "weathers" private val pathToImage = "weathers"
fun readFromJson(modname: String, path: String) { fun readFromJson(modname: String, path: String) {
/* JSON structure: readFromJsonValue(modname, JsonFetcher(path))
{ }
"skyboxGradColourMap": "colourmap/sky_colour.tga", // string (path to image) for dynamic. Image must be RGBA8888 or RGB888
"extraImages": [
// if any, it will be like:
sun01.tga,
clouds01.tga,
clouds02.tga,
auroraBlueViolet.tga
]
}
*/
val JSON = JsonFetcher(path)
private fun readFromJsonValue(modname: String, JSON: com.badlogic.gdx.utils.JsonValue) {
val skyboxModel = JSON.getString("skyboxGradColourMap") val skyboxModel = JSON.getString("skyboxGradColourMap")
val lightboxModel = JSON.getString("daylightClut") val lightboxModel = JSON.getString("daylightClut")