attempt js

This commit is contained in:
RandomityGuy 2021-07-08 17:40:50 +05:30
parent 16b18da90c
commit cbd54a9bd6
17 changed files with 895 additions and 44 deletions

8
compile-js.hxml Normal file
View file

@ -0,0 +1,8 @@
-cp src
-lib heaps
-lib stb_ogg_sound
--js marblegame.js
-D windowSize=1280x720
-D keep-inline-positions
--main Main
-debug

1
data/filesystem.manifest Normal file

File diff suppressed because one or more lines are too long

23
index.html Normal file
View file

@ -0,0 +1,23 @@
<!DOCTYPE>
<html>
<head>
<meta charset="utf-8"/>
<title>Hello Heaps</title>
<style>
body {
margin: 0;
padding: 0;
background-color: black;
}
canvas#webgl {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="webgl"></canvas>
<script type="text/javascript" src="marblegame.js"></script>
</body>
</html>

Binary file not shown.

View file

@ -43,14 +43,24 @@ class AudioManager {
}
public static function playShell() {
#if js
return;
#end
AudioManager.manager.stopByName("music");
var snd = ResourceLoader.getAudio("data/sound/shell.ogg");
if (snd == null)
return;
var ch = AudioManager.manager.play(snd, null, musicGroup);
ch.loop = true;
}
public static function playMusic(music:Sound) {
#if js
return;
#end
AudioManager.manager.stopByName("music");
if (music == null)
return;
var ch = AudioManager.manager.play(music, null, musicGroup);
ch.loop = true;
}

View file

@ -84,25 +84,18 @@ class CameraController extends Object {
#end
}
function onEvent(e:sdl.Event) {
switch (e.type) {
case MouseMove:
if (this.level.cursorLock) {
orbit(e.mouseXRel, e.mouseYRel);
}
default:
}
return true;
}
public function lockCursor() {
Window.getInstance().lockPointer((x, y) -> orbit(x, y));
#if hl
Cursor.show(false);
#end
}
public function unlockCursor() {
Window.getInstance().unlockPointer();
#if hl
Cursor.show(true);
#end
}
function orbit(mouseX:Float, mouseY:Float) {

View file

@ -224,18 +224,18 @@ class DifBuilder {
tex = tex.split('/')[1];
}
if (File.exists(Path.directory(path) + "/" + tex + ".jpg")) {
if (ResourceLoader.fileSystem.exists(Path.directory(path) + "/" + tex + ".jpg")) {
return true;
}
if (File.exists(Path.directory(path) + "/" + tex + ".png")) {
if (ResourceLoader.fileSystem.exists(Path.directory(path) + "/" + tex + ".png")) {
return true;
}
var prevDir = Path.directory(Path.directory(path));
if (File.exists(prevDir + "/" + tex + ".jpg")) {
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".jpg")) {
return true;
}
if (File.exists(prevDir + "/" + tex + ".png")) {
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".png")) {
return true;
}
@ -247,19 +247,19 @@ class DifBuilder {
tex = tex.split('/')[1];
}
if (File.exists(Path.directory(path) + "/" + tex + ".jpg")) {
if (ResourceLoader.fileSystem.exists(Path.directory(path) + "/" + tex + ".jpg")) {
return Path.directory(path) + "/" + tex + ".jpg";
}
if (File.exists(Path.directory(path) + "/" + tex + ".png")) {
if (ResourceLoader.fileSystem.exists(Path.directory(path) + "/" + tex + ".png")) {
return Path.directory(path) + "/" + tex + ".png";
}
var prevDir = Path.directory(Path.directory(path));
if (File.exists(prevDir + "/" + tex + ".jpg")) {
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".jpg")) {
return prevDir + "/" + tex + ".jpg";
}
if (File.exists(prevDir + "/" + tex + ".png")) {
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".png")) {
return prevDir + "/" + tex + ".png";
}

View file

@ -5,7 +5,6 @@ import shaders.Billboard;
import collision.BoxCollisionEntity;
import shaders.DtsTexture;
import h3d.shader.AlphaMult;
import sys.io.File;
import src.MarbleWorld;
import src.GameObject;
import collision.CollisionHull;
@ -427,7 +426,7 @@ class DtsObject extends GameObject {
}
function parseIfl(path:String) {
var text = File.getContent(path);
var text = ResourceLoader.fileSystem.get(path).getText();
var lines = text.split('\n');
var keyframes = [];
for (line in lines) {

View file

@ -1,5 +1,7 @@
package;
import src.ResourceLoader;
import fs.ManifestFileSystem;
import hxd.Timer;
import hxd.Window;
import src.AudioManager;
@ -9,43 +11,53 @@ import gui.MainMenuGui;
import hxd.res.DefaultFont;
import h2d.Text;
import h3d.Vector;
import fs.ManifestBuilder;
class Main extends hxd.App {
var marbleGame:MarbleGame;
var fpsCounter:Text;
var loaded:Bool = false;
override function init() {
super.init();
#if hl
hl.UI.closeConsole();
#end
Settings.init();
AudioManager.init();
AudioManager.playShell();
marbleGame = new MarbleGame(s2d, s3d);
MarbleGame.canvas.setContent(new MainMenuGui());
// world = new MarbleWorld(s3d, s2d, mission);
ResourceLoader.init(() -> {
Settings.init();
AudioManager.init();
AudioManager.playShell();
marbleGame = new MarbleGame(s2d, s3d);
MarbleGame.canvas.setContent(new MainMenuGui());
// world = new MarbleWorld(s3d, s2d, mission);
// world.init();
// world.start();
// world.init();
// world.start();
fpsCounter = new Text(DefaultFont.get(), s2d);
fpsCounter.y = 40;
fpsCounter.color = new Vector(1, 1, 1, 1);
fpsCounter = new Text(DefaultFont.get(), s2d);
fpsCounter.y = 40;
fpsCounter.color = new Vector(1, 1, 1, 1);
loaded = true;
});
}
override function update(dt:Float) {
super.update(dt);
marbleGame.update(dt);
// world.update(dt);
fpsCounter.text = 'FPS: ${this.engine.fps}';
if (loaded) {
marbleGame.update(dt);
// world.update(dt);
fpsCounter.text = 'FPS: ${this.engine.fps}';
}
}
override function render(e:h3d.Engine) {
// this.world.render(e);
marbleGame.render(e);
if (loaded)
marbleGame.render(e);
super.render(e);
}

View file

@ -1,11 +1,11 @@
package src;
import fs.ManifestLoader;
import fs.ManifestBuilder;
import hxd.res.Image;
import hxd.res.Sound;
import h3d.mat.Texture;
import h3d.scene.Object;
import sys.FileSystem;
import sys.io.File;
import haxe.io.Path;
import dts.DtsFile;
import dif.Dif;
@ -14,8 +14,18 @@ import hxd.fs.FileSystem;
import hxd.res.Loader;
class ResourceLoader {
#if hl
public static var fileSystem:FileSystem = new LocalFileSystem(".", null);
#end
#if js
public static var fileSystem:FileSystem = null;
#end
#if hl
public static var loader = new Loader(fileSystem);
#end
#if js
public static var loader:Loader = null;
#end
static var interiorResources:Map<String, Dif> = new Map();
static var dtsResources:Map<String, DtsFile> = new Map();
static var textureCache:Map<String, Texture> = new Map();
@ -24,14 +34,34 @@ class ResourceLoader {
// static var threadPool:FixedThreadPool = new FixedThreadPool(4);
public static function init(onLoadedFunc:Void->Void) {
#if js
var mfileSystem = ManifestBuilder.create("data");
var mloader = new ManifestLoader(mfileSystem);
mloader.onLoaded = () -> {
loader = mloader;
fileSystem = mfileSystem;
onLoadedFunc();
};
mloader.loadManifestFiles();
#end
#if hl
onLoadedFunc();
#end
}
public static function loadInterior(path:String) {
#if js
path = StringTools.replace(path, "data/", "");
#end
if (interiorResources.exists(path))
return interiorResources.get(path);
else {
var itr:Dif;
// var lock = new Lock();
// threadPool.run(() -> {
itr = Dif.Load(path);
itr = Dif.LoadFromBuffer(fileSystem.get(path).getBytes());
interiorResources.set(path, itr);
// lock.release();
// });
@ -41,6 +71,9 @@ class ResourceLoader {
}
public static function loadDts(path:String) {
#if js
path = StringTools.replace(path, "data/", "");
#end
if (dtsResources.exists(path))
return dtsResources.get(path);
else {
@ -57,6 +90,9 @@ class ResourceLoader {
}
public static function getTexture(path:String) {
#if js
path = StringTools.replace(path, "data/", "");
#end
if (textureCache.exists(path))
return textureCache.get(path);
if (fileSystem.exists(path)) {
@ -69,6 +105,9 @@ class ResourceLoader {
}
public static function getImage(path:String) {
#if js
path = StringTools.replace(path, "data/", "");
#end
if (imageCache.exists(path))
return imageCache.get(path);
if (fileSystem.exists(path)) {
@ -80,6 +119,9 @@ class ResourceLoader {
}
public static function getAudio(path:String) {
#if js
path = StringTools.replace(path, "data/", "");
#end
if (audioCache.exists(path))
return audioCache.get(path);
if (fileSystem.exists(path)) {
@ -90,6 +132,14 @@ class ResourceLoader {
return null;
}
public static function getFile(path:String) {
#if js
path = StringTools.replace(path, "data/", "");
#end
var file = fileSystem.get(path);
return file;
}
public static function clearInteriorResources() {
interiorResources = new Map();
}

View file

@ -5,7 +5,9 @@ import hxd.Key;
import src.MarbleGame;
import hxd.Window;
import haxe.DynamicAccess;
#if hl
import sys.io.File;
#end
import src.ResourceLoader;
import haxe.Json;
@ -115,7 +117,9 @@ class Settings {
highscoreName: highscoreName
};
var json = Json.stringify(outputData);
#if hl
File.saveContent("settings.json", json);
#end
}
public static function load() {

View file

@ -10,7 +10,6 @@ import hxd.BitmapData;
import h3d.shader.CubeMap;
import h3d.mat.Texture;
import haxe.io.Path;
import sys.io.File;
import src.ResourceLoader;
import h3d.scene.Object;
@ -50,7 +49,7 @@ class Sky extends Object {
function createSkyboxCubeTextured(dmlPath:String) {
if (ResourceLoader.fileSystem.exists(dmlPath)) {
var dmlFile = File.getContent(dmlPath);
var dmlFile = ResourceLoader.fileSystem.get(dmlPath).getText();
var dmlDirectory = Path.directory(dmlPath);
var lines = dmlFile.split('\n').map(x -> x.toLowerCase());
var skyboxImages = [];

View file

@ -1,11 +1,11 @@
package dts;
import haxe.Exception;
import sys.io.File;
import dif.io.BytesReader;
import dif.math.QuatF;
import dif.math.Box3F;
import dif.math.Point3F;
import src.ResourceLoader;
@:publicFields
class DtsFile {
@ -61,8 +61,8 @@ class DtsFile {
public function new() {}
public function read(filepath:String) {
var f = File.read(filepath);
var bytes = f.readAll();
var f = ResourceLoader.fileSystem.get(filepath);
var bytes = f.getBytes();
var br = new BytesReader(bytes);
fileVersion = br.readInt16();

160
src/fs/ManifestBuilder.hx Normal file
View file

@ -0,0 +1,160 @@
// MIT License
// Copyright (c) 2018 Pavel Alexandrov
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package fs;
import hxd.res.EmbedOptions;
import haxe.io.Path;
import haxe.io.Bytes;
typedef ManifestFileInfo = {
var path:String;
var original:String;
}
class ManifestBuilder {
public static macro function initManifest(?basePath:String, ?options:hxd.res.EmbedOptions, ?storeManifest:String) {
var data = makeManifest(basePath, options, storeManifest);
if (basePath == null)
basePath = "res";
return macro {
var loader = new fs.ManifestLoader(@:privateAccess new fs.ManifestFileSystem($v{basePath}, haxe.io.Bytes.ofString($v{data.manifest.toString()})));
hxd.Res.loader = loader;
loader;
}
}
public static macro function generate(?basePath:String, ?options:hxd.res.EmbedOptions, ?storeManifest:String) {
var data = makeManifest(basePath, options, storeManifest);
return macro {};
}
public static macro function create(?basePath:String, ?options:hxd.res.EmbedOptions, ?storeManifest:String) {
var data = makeManifest(basePath, options, storeManifest);
// var types = {
// expr : haxe.macro.Expr.ExprDef.EBlock([for( t in data.types ) haxe.macro.MacroStringTools.toFieldExpr(t.split("."))]),
// pos : haxe.macro.Context.currentPos(),
// };
return macro @:privateAccess new fs.ManifestFileSystem($v{basePath}, haxe.io.Bytes.ofString($v{data.manifest.toString()}));
}
#if macro
private static function makeManifest(?basePath:String, ?options:hxd.res.EmbedOptions, ?storeManifest:String) {
var f = new hxd.res.FileTree(basePath);
var manifest:Bytes = build(f, options);
if (storeManifest != null) {
if (!haxe.macro.Context.defined("display")) {
#if !display
var tmp:String = Path.join([@:privateAccess f.paths[0], storeManifest + ".manifest"]);
sys.io.File.saveBytes(tmp, manifest);
#end
}
}
return {tree: f, manifest: manifest};
}
// private static function makeTree(t:hxd.res.FileTree.FileTreeData, out:Array<ManifestFileInfo>):Void
// {
// for (f in t.files) out.push(f.relPath);
// for (d in t.dirs) makeTree(d, out);
// }
public static function build(tree:hxd.res.FileTree, ?options:hxd.res.EmbedOptions, ?manifestOptions:ManifestOptions):Bytes {
var manifest:Array<ManifestFileInfo> = new Array();
var data = scan(tree, options, manifest);
// makeTree(data, manifest);
if (manifestOptions == null) {
manifestOptions = {format: ManifestFormat.Json};
}
if (manifestOptions.format == null)
manifestOptions.format = ManifestFormat.List;
switch (manifestOptions.format) {
// case ManifestFormat.List:
// return haxe.io.Bytes.ofString("l\n" + manifest.join("\n"));
case Json:
return haxe.io.Bytes.ofString(haxe.Json.stringify(manifest));
case v:
// case ManifestFormat.KeyValue:
// case ManifestFormat.Serialized:
// case ManifestFormat.Json:
throw "unsupported manifest foramt: " + v;
}
}
static var options:EmbedOptions;
static function scan(t:hxd.res.FileTree, options:EmbedOptions, out:Array<ManifestFileInfo>) {
if (options == null)
options = {};
// var setTmp = options.tmpDir == null;
// if( options.compressAsMp3 == null ) options.compressAsMp3 = options.compressSounds && !(haxe.macro.Context.defined("stb_ogg_sound") || hxd.res.Config.platform == HL);
ManifestBuilder.options = options;
var tree = @:privateAccess t.scan();
for (path in @:privateAccess t.paths) {
// if( setTmp ) options.tmpDir = path + "/.tmp/";
var fs = new hxd.fs.LocalFileSystem(path, options.configuration);
// if( options.compressAsMp3 )
// fs.addConvert(new hxd.fs.Convert.ConvertWAV2MP3());
// else if( options.compressSounds )
// fs.addConvert(new hxd.fs.Convert.ConvertWAV2MP3());
// fs.tmpDir = options.tmpDir;
fs.convert.onConvert = function(f) Sys.println("Converting " + f.srcPath);
convertRec(tree, path, fs, out);
}
return tree;
}
static function convertRec(tree:hxd.res.FileTree.FileTreeData, basePath:String, fs:hxd.fs.LocalFileSystem, out:Array<ManifestFileInfo>) {
@:privateAccess for (file in tree.files) {
// try later with another fs
if (!StringTools.startsWith(file.fullPath, basePath))
continue;
var info = {path: file.relPath, original: file.relPath};
out.push(info);
var f = fs.get(file.relPath); // convert
if (f.originalFile != null && f.originalFile != f.file) {
info.original = f.relPath;
info.path = StringTools.startsWith(f.file, fs.baseDir) ? f.file.substr(fs.baseDir.length) : f.file;
}
}
for (t in tree.dirs)
convertRec(t, basePath, fs, out);
}
#end
}
typedef ManifestOptions = {
@:optional var format:ManifestFormat;
}
enum ManifestFormat {
KeyValue;
List;
Serialized;
Json;
}

View file

@ -0,0 +1,459 @@
// MIT License
// Copyright (c) 2018 Pavel Alexandrov
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package fs;
import hxd.net.BinaryLoader;
import hxd.impl.ArrayIterator;
import hxd.fs.LoadedBitmap;
import hxd.fs.NotFound;
import hxd.fs.FileEntry;
import hxd.fs.FileSystem;
import haxe.io.Encoding;
import haxe.io.Path;
import haxe.io.Bytes;
@:allow(fs.ManifestFileSystem)
class ManifestEntry extends FileEntry {
private var fs:ManifestFileSystem;
private var relPath:String;
private var isDir:Bool;
private var contents:Array<ManifestEntry>;
private var file:String;
private var originalFile:String;
#if sys
private var fio:sys.io.FileInput;
#else
private var bytes:Bytes;
private var readPos:Int;
private var loaded:Bool;
#end
public function new(fs:ManifestFileSystem, name:String, relPath:String, file:String, ?originalFile:String) {
this.fs = fs;
this.name = name;
this.relPath = relPath;
this.originalFile = originalFile;
this.file = file;
if (file == null) {
isDir = true;
contents = new Array();
}
}
override public function getSign():Int {
#if sys
var old = if (fio == null) -1 else fio.tell();
open();
var i = fio.readInt32();
if (old < 0)
close()
else
fio.seek(old, SeekBegin);
return i;
#else
return bytes.get(0) | (bytes.get(1) << 8) | (bytes.get(2) << 16) | (bytes.get(3) << 24);
#end
}
override public function getBytes():Bytes {
#if sys
return sys.io.File.getBytes(file);
#else
return bytes;
#end
}
override public function open() {
#if sys
if (fio == null)
fio = sys.io.File.read(file);
else
fio.seek(0, SeekBegin);
#else
readPos = 0;
#end
}
override public function skip(nbytes:Int) {
#if sys
fio.seek(nbytes, SeekCur);
#else
readPos += nbytes;
if (bytes.length < readPos)
readPos = bytes.length;
#end
}
override public function readByte():Int {
#if sys
return fio.readByte();
#else
return bytes.get(readPos++);
#end
}
override public function read(out:Bytes, pos:Int, size:Int) {
#if sys
fio.readFullBytes(out, pos, size);
#else
out.blit(pos, bytes, readPos, size);
readPos += size;
#end
}
override public function close() {
#if sys
if (fio != null) {
fio.close();
fio = null;
}
#else
readPos = 0;
#end
}
public function fancyLoad(onReady:() -> Void, onProgress:(cur:Int, max:Int) -> Void) {
#if js
if (loaded) {
haxe.Timer.delay(onReady, 1);
} else {
var br:BinaryLoader = new BinaryLoader(file);
br.onLoaded = (b) -> {
loaded = true;
bytes = b;
onReady();
}
br.onProgress = onProgress;
br.load();
}
#else
load(onReady);
#end
}
override public function load(?onReady:() -> Void) {
#if macro
onReady();
#elseif js
if (loaded) {
if (onReady != null)
haxe.Timer.delay(onReady, 1);
} else {
js.Browser.window.fetch(file).then((res:js.html.Response) -> {
return res.arrayBuffer();
}).then((buf:js.lib.ArrayBuffer) -> {
loaded = true;
bytes = Bytes.ofData(buf);
if (onReady != null)
onReady();
});
}
#else
if (onReady != null)
haxe.Timer.delay(onReady, 1);
#end
}
override public function loadBitmap(onLoaded:LoadedBitmap->Void) {
#if sys
var bmp = new hxd.res.Image(this).toBitmap();
onLoaded(new hxd.fs.LoadedBitmap(bmp));
#elseif js
load(() -> {
var img:js.html.Image = new js.html.Image();
img.onload = (_) -> onLoaded(new LoadedBitmap(img));
img.src = file;
});
#else
throw "Unsupported platform";
#end
}
override public function exists(name:String):Bool {
return _exists(name);
}
override public function get(name:String):FileEntry {
return _get(name);
}
private function _exists(name:String):Bool {
if (isDir) {
for (c in contents)
if (c.name == name)
return true;
}
return false;
}
private function _get(name:String):ManifestEntry {
if (isDir) {
for (c in contents)
if (c.name == name)
return c;
}
return null;
}
override public function iterator():ArrayIterator<FileEntry> {
if (isDir)
return new ArrayIterator(cast contents);
return null;
}
#if !sys
override private function get_isAvailable():Bool {
return loaded;
}
#end
override private function get_isDirectory():Bool {
return isDir;
}
override private function get_path():String {
return relPath == "." ? "<root>" : relPath;
}
override private function get_size():Int {
#if sys
return sys.FileSystem.stat(file).size;
#else
return bytes != null ? bytes.length : 0;
#end
}
private function dispose():Void {
if (isDir) {
for (c in contents)
c.dispose();
contents = null;
}
#if sys
close();
#else
bytes = null;
#end
}
}
class ManifestFileSystem implements FileSystem {
private var baseDir:String;
public var manifest:Map<String, ManifestEntry>;
var root:ManifestEntry;
public function new(dir:String, _manifest:Bytes) {
this.baseDir = Path.addTrailingSlash(dir);
this.root = new ManifestEntry(this, "<root>", ".", null);
this.manifest = new Map();
inline function insert(path:String, file:String, original:String):Void {
var dir:Array<String> = Path.directory(original).split('/');
var r:ManifestEntry = root;
for (n in dir) {
if (n == "")
continue;
var found:Bool = false;
for (c in r.contents) {
if (c.name == n) {
r = c;
found = true;
break;
}
}
if (!found) {
var dirEntry = new ManifestEntry(this, n, r.relPath + "/" + n, null);
r.contents.push(dirEntry);
r = dirEntry;
}
}
var entry:ManifestEntry = new ManifestEntry(this, Path.withoutDirectory(original), original, file, original);
r.contents.push(entry);
manifest.set(path, entry);
}
switch (_manifest.get(0)) {
case 0:
// binary
throw "Binary manifest not yet supported!";
case 1:
// serialized
throw "Serialized manifest not yet supported!";
case 'm'.code:
// id:path mapping
throw "Mapping manifest not yet supported";
// var mapping:Array<String> = _manifest.getString(2, _manifest.length - 2, Encoding.UTF8).split("\n");
// for (map in mapping)
// {
// var idx:Int = map.indexOf(":");
// insert(map.substr(0, idx), baseDir + map.substr(idx+1));
// }
case 'l'.code:
// path mapping
throw "List manifest not yet supported";
// var mapping:Array<String> = _manifest.getString(2, _manifest.length - 2, Encoding.UTF8).split("\n");
// for (path in mapping)
// {
// insert(path, baseDir + path);
// }
case '['.code:
// JSON
var json:Array<{path:String, original:String}> = haxe.Json.parse(_manifest.toString());
for (entry in json) {
insert(entry.path, baseDir + entry.path, entry.original);
}
}
}
public function getRoot():FileEntry {
return root;
}
private function splitPath(path:String) {
return path == "." ? [] : path.split("/");
}
private function find(path:String):ManifestEntry {
var r = root;
for (p in splitPath(path)) {
r = r._get(p);
if (r == null)
return null;
}
return r;
}
public function exists(path:String) {
return find(path) != null;
}
public function get(path:String) {
var entry:ManifestEntry = find(path);
if (entry == null)
throw new NotFound(path);
return entry;
}
public function dispose() {
root.dispose();
root = null;
}
public function dir(path:String):Array<FileEntry> {
var entry:ManifestEntry = find(path);
if (entry == null)
throw new NotFound(path);
return cast entry.contents.copy();
}
}
/*
using haxe.io.Path;
class BytesFileEntry extends FileEntry {
var fullPath : String;
var bytes : haxe.io.Bytes;
var pos : Int;
public function new(path, bytes) {
this.fullPath = path;
this.name = path.split("/").pop();
this.bytes = bytes;
}
override function get_path() {
return fullPath;
}
override function getSign() : Int {
return bytes.get(0) | (bytes.get(1) << 8) | (bytes.get(2) << 16) | (bytes.get(3) << 24);
}
override function getBytes() : haxe.io.Bytes {
return bytes;
}
override function open() {
pos = 0;
}
override function skip( nbytes : Int ) {
pos += nbytes;
}
override function readByte() : Int {
return bytes.get(pos++);
}
override function read( out : haxe.io.Bytes, pos : Int, size : Int ) {
out.blit(pos, bytes, this.pos, size);
this.pos += size;
}
override function close() {
}
override function load( ?onReady : Void -> Void ) : Void {
haxe.Timer.delay(onReady, 1);
}
override function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void {
#if flash
var loader = new flash.display.Loader();
loader.contentLoaderInfo.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e:flash.events.IOErrorEvent) {
throw Std.string(e) + " while loading " + fullPath;
});
loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, function(_) {
var content : flash.display.Bitmap = cast loader.content;
onLoaded(new hxd.fs.LoadedBitmap(content.bitmapData));
loader.unload();
});
loader.loadBytes(bytes.getData());
#elseif js
var mime = switch fullPath.extension().toLowerCase() {
case 'jpg' | 'jpeg': 'image/jpeg';
case 'png': 'image/png';
case 'gif': 'image/gif';
case _: throw 'Cannot determine image encoding, try adding an extension to the resource path';
}
var img = new js.html.Image();
img.onload = function() onLoaded(new hxd.fs.LoadedBitmap(img));
img.src = 'data:$mime;base64,' + haxe.crypto.Base64.encode(bytes);
#else
throw "Not implemented";
#end
}
override function exists( name : String ) : Bool return false;
override function get( name : String ) : FileEntry return null;
override function iterator() : hxd.impl.ArrayIterator<FileEntry> return new hxd.impl.ArrayIterator(new Array<FileEntry>());
override function get_size() return bytes.length;
}
class BytesFileSystem implements FileSystem {
}*/

131
src/fs/ManifestLoader.hx Normal file
View file

@ -0,0 +1,131 @@
// MIT License
// Copyright (c) 2018 Pavel Alexandrov
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package fs;
import fs.ManifestFileSystem.ManifestEntry;
@:allow(hxd.res.ManifestLoader.LoaderTask)
class ManifestLoader extends hxd.res.Loader {
/**
Amount of concurrent file loadings. Defaults to 4 on JS and to 1 native (since there's no threaded loading implemented for native)
**/
public static var concurrentFiles:Int = #if js 4 #else 1 #end;
public var mfs:ManifestFileSystem;
public var totalFiles(default, null):Int;
public var loadedFiles(default, null):Int;
public var loading(default, null):Bool;
var entries:Iterator<ManifestEntry>;
var current:ManifestEntry;
/**
List of loading tasks used during loading.
**/
public var tasks:Array<LoaderTask>;
public function new(fs:ManifestFileSystem) {
super(fs);
mfs = fs;
totalFiles = 0;
for (f in fs.manifest)
totalFiles++;
loadedFiles = 0;
loading = false;
}
public function loadManifestFiles() {
if (!loading) {
tasks = new Array();
for (i in 0...concurrentFiles)
tasks.push(@:privateAccess new LoaderTask(i, this));
loading = true;
entries = mfs.manifest.iterator();
for (t in tasks) {
if (entries.hasNext())
t.load(entries.next());
else
break;
}
}
}
private function next(task:LoaderTask):Void {
loadedFiles++;
onFileLoaded(task);
if (entries.hasNext())
task.load(entries.next());
else {
for (t in tasks)
if (t.busy)
return;
loading = false;
tasks = null;
onLoaded();
}
}
// Called when loader starts loading of specific file.
public dynamic function onFileLoadStarted(task:LoaderTask):Void {}
// Called during file loading. loaded and total refer to loaded bytes and total file size.
public dynamic function onFileProgress(task:LoaderTask):Void {}
// Called when file is loaded.
public dynamic function onFileLoaded(task:LoaderTask):Void {}
public dynamic function onLoaded():Void {}
}
class LoaderTask {
public var entry(default, null):ManifestEntry;
/** Loading slot occupied by this task **/
public var slot(default, null):Int;
public var loaded(default, null):Int;
public var total(default, null):Int;
public var owner(default, null):ManifestLoader;
public var busy(default, null):Bool;
function new(slot:Int, owner:ManifestLoader) {
this.slot = slot;
this.owner = owner;
}
public function load(entry:ManifestEntry):Void {
this.entry = entry;
this.loaded = 0;
this.total = 1;
busy = true;
owner.onFileLoadStarted(this);
entry.fancyLoad(ready, progress);
}
function ready() {
busy = false;
@:privateAccess owner.next(this);
}
function progress(l:Int, t:Int) {
loaded = l;
total = t;
owner.onFileProgress(this);
}
}

View file

@ -68,7 +68,9 @@ class MainMenuGui extends GuiImage {
exitButton.position = new Vector(82, 358);
exitButton.extent = new Vector(203, 88);
exitButton.pressedAction = (sender) -> {
#if hl
Sys.exit(0);
#end
};
homebase.addChild(exitButton);
}