mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
attempt js
This commit is contained in:
parent
16b18da90c
commit
cbd54a9bd6
17 changed files with 895 additions and 44 deletions
8
compile-js.hxml
Normal file
8
compile-js.hxml
Normal 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
1
data/filesystem.manifest
Normal file
File diff suppressed because one or more lines are too long
23
index.html
Normal file
23
index.html
Normal 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>
|
||||
BIN
marblegame.hl
BIN
marblegame.hl
Binary file not shown.
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
42
src/Main.hx
42
src/Main.hx
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 = [];
|
||||
|
|
|
|||
|
|
@ -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
160
src/fs/ManifestBuilder.hx
Normal 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;
|
||||
}
|
||||
459
src/fs/ManifestFileSystem.hx
Normal file
459
src/fs/ManifestFileSystem.hx
Normal 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
131
src/fs/ManifestLoader.hx
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue