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() {
|
public static function playShell() {
|
||||||
|
#if js
|
||||||
|
return;
|
||||||
|
#end
|
||||||
AudioManager.manager.stopByName("music");
|
AudioManager.manager.stopByName("music");
|
||||||
var snd = ResourceLoader.getAudio("data/sound/shell.ogg");
|
var snd = ResourceLoader.getAudio("data/sound/shell.ogg");
|
||||||
|
if (snd == null)
|
||||||
|
return;
|
||||||
var ch = AudioManager.manager.play(snd, null, musicGroup);
|
var ch = AudioManager.manager.play(snd, null, musicGroup);
|
||||||
ch.loop = true;
|
ch.loop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function playMusic(music:Sound) {
|
public static function playMusic(music:Sound) {
|
||||||
|
#if js
|
||||||
|
return;
|
||||||
|
#end
|
||||||
AudioManager.manager.stopByName("music");
|
AudioManager.manager.stopByName("music");
|
||||||
|
if (music == null)
|
||||||
|
return;
|
||||||
var ch = AudioManager.manager.play(music, null, musicGroup);
|
var ch = AudioManager.manager.play(music, null, musicGroup);
|
||||||
ch.loop = true;
|
ch.loop = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,25 +84,18 @@ class CameraController extends Object {
|
||||||
#end
|
#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() {
|
public function lockCursor() {
|
||||||
Window.getInstance().lockPointer((x, y) -> orbit(x, y));
|
Window.getInstance().lockPointer((x, y) -> orbit(x, y));
|
||||||
|
#if hl
|
||||||
Cursor.show(false);
|
Cursor.show(false);
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unlockCursor() {
|
public function unlockCursor() {
|
||||||
Window.getInstance().unlockPointer();
|
Window.getInstance().unlockPointer();
|
||||||
|
#if hl
|
||||||
Cursor.show(true);
|
Cursor.show(true);
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
function orbit(mouseX:Float, mouseY:Float) {
|
function orbit(mouseX:Float, mouseY:Float) {
|
||||||
|
|
|
||||||
|
|
@ -224,18 +224,18 @@ class DifBuilder {
|
||||||
tex = tex.split('/')[1];
|
tex = tex.split('/')[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.exists(Path.directory(path) + "/" + tex + ".jpg")) {
|
if (ResourceLoader.fileSystem.exists(Path.directory(path) + "/" + tex + ".jpg")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (File.exists(Path.directory(path) + "/" + tex + ".png")) {
|
if (ResourceLoader.fileSystem.exists(Path.directory(path) + "/" + tex + ".png")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
var prevDir = Path.directory(Path.directory(path));
|
var prevDir = Path.directory(Path.directory(path));
|
||||||
|
|
||||||
if (File.exists(prevDir + "/" + tex + ".jpg")) {
|
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".jpg")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (File.exists(prevDir + "/" + tex + ".png")) {
|
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".png")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,19 +247,19 @@ class DifBuilder {
|
||||||
tex = tex.split('/')[1];
|
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";
|
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";
|
return Path.directory(path) + "/" + tex + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
var prevDir = Path.directory(Path.directory(path));
|
var prevDir = Path.directory(Path.directory(path));
|
||||||
|
|
||||||
if (File.exists(prevDir + "/" + tex + ".jpg")) {
|
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".jpg")) {
|
||||||
return prevDir + "/" + tex + ".jpg";
|
return prevDir + "/" + tex + ".jpg";
|
||||||
}
|
}
|
||||||
if (File.exists(prevDir + "/" + tex + ".png")) {
|
if (ResourceLoader.fileSystem.exists(prevDir + "/" + tex + ".png")) {
|
||||||
return prevDir + "/" + tex + ".png";
|
return prevDir + "/" + tex + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import shaders.Billboard;
|
||||||
import collision.BoxCollisionEntity;
|
import collision.BoxCollisionEntity;
|
||||||
import shaders.DtsTexture;
|
import shaders.DtsTexture;
|
||||||
import h3d.shader.AlphaMult;
|
import h3d.shader.AlphaMult;
|
||||||
import sys.io.File;
|
|
||||||
import src.MarbleWorld;
|
import src.MarbleWorld;
|
||||||
import src.GameObject;
|
import src.GameObject;
|
||||||
import collision.CollisionHull;
|
import collision.CollisionHull;
|
||||||
|
|
@ -427,7 +426,7 @@ class DtsObject extends GameObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseIfl(path:String) {
|
function parseIfl(path:String) {
|
||||||
var text = File.getContent(path);
|
var text = ResourceLoader.fileSystem.get(path).getText();
|
||||||
var lines = text.split('\n');
|
var lines = text.split('\n');
|
||||||
var keyframes = [];
|
var keyframes = [];
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
|
|
|
||||||
42
src/Main.hx
42
src/Main.hx
|
|
@ -1,5 +1,7 @@
|
||||||
package;
|
package;
|
||||||
|
|
||||||
|
import src.ResourceLoader;
|
||||||
|
import fs.ManifestFileSystem;
|
||||||
import hxd.Timer;
|
import hxd.Timer;
|
||||||
import hxd.Window;
|
import hxd.Window;
|
||||||
import src.AudioManager;
|
import src.AudioManager;
|
||||||
|
|
@ -9,43 +11,53 @@ import gui.MainMenuGui;
|
||||||
import hxd.res.DefaultFont;
|
import hxd.res.DefaultFont;
|
||||||
import h2d.Text;
|
import h2d.Text;
|
||||||
import h3d.Vector;
|
import h3d.Vector;
|
||||||
|
import fs.ManifestBuilder;
|
||||||
|
|
||||||
class Main extends hxd.App {
|
class Main extends hxd.App {
|
||||||
var marbleGame:MarbleGame;
|
var marbleGame:MarbleGame;
|
||||||
|
|
||||||
var fpsCounter:Text;
|
var fpsCounter:Text;
|
||||||
|
|
||||||
|
var loaded:Bool = false;
|
||||||
|
|
||||||
override function init() {
|
override function init() {
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
#if hl
|
#if hl
|
||||||
hl.UI.closeConsole();
|
hl.UI.closeConsole();
|
||||||
#end
|
#end
|
||||||
Settings.init();
|
ResourceLoader.init(() -> {
|
||||||
AudioManager.init();
|
Settings.init();
|
||||||
AudioManager.playShell();
|
AudioManager.init();
|
||||||
marbleGame = new MarbleGame(s2d, s3d);
|
AudioManager.playShell();
|
||||||
MarbleGame.canvas.setContent(new MainMenuGui());
|
marbleGame = new MarbleGame(s2d, s3d);
|
||||||
// world = new MarbleWorld(s3d, s2d, mission);
|
MarbleGame.canvas.setContent(new MainMenuGui());
|
||||||
|
// world = new MarbleWorld(s3d, s2d, mission);
|
||||||
|
|
||||||
// world.init();
|
// world.init();
|
||||||
// world.start();
|
// world.start();
|
||||||
|
|
||||||
fpsCounter = new Text(DefaultFont.get(), s2d);
|
fpsCounter = new Text(DefaultFont.get(), s2d);
|
||||||
fpsCounter.y = 40;
|
fpsCounter.y = 40;
|
||||||
fpsCounter.color = new Vector(1, 1, 1, 1);
|
fpsCounter.color = new Vector(1, 1, 1, 1);
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(dt:Float) {
|
override function update(dt:Float) {
|
||||||
super.update(dt);
|
super.update(dt);
|
||||||
marbleGame.update(dt);
|
if (loaded) {
|
||||||
// world.update(dt);
|
marbleGame.update(dt);
|
||||||
fpsCounter.text = 'FPS: ${this.engine.fps}';
|
// world.update(dt);
|
||||||
|
fpsCounter.text = 'FPS: ${this.engine.fps}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override function render(e:h3d.Engine) {
|
override function render(e:h3d.Engine) {
|
||||||
// this.world.render(e);
|
// this.world.render(e);
|
||||||
marbleGame.render(e);
|
if (loaded)
|
||||||
|
marbleGame.render(e);
|
||||||
super.render(e);
|
super.render(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import fs.ManifestLoader;
|
||||||
|
import fs.ManifestBuilder;
|
||||||
import hxd.res.Image;
|
import hxd.res.Image;
|
||||||
import hxd.res.Sound;
|
import hxd.res.Sound;
|
||||||
import h3d.mat.Texture;
|
import h3d.mat.Texture;
|
||||||
import h3d.scene.Object;
|
import h3d.scene.Object;
|
||||||
import sys.FileSystem;
|
|
||||||
import sys.io.File;
|
|
||||||
import haxe.io.Path;
|
import haxe.io.Path;
|
||||||
import dts.DtsFile;
|
import dts.DtsFile;
|
||||||
import dif.Dif;
|
import dif.Dif;
|
||||||
|
|
@ -14,8 +14,18 @@ import hxd.fs.FileSystem;
|
||||||
import hxd.res.Loader;
|
import hxd.res.Loader;
|
||||||
|
|
||||||
class ResourceLoader {
|
class ResourceLoader {
|
||||||
|
#if hl
|
||||||
public static var fileSystem:FileSystem = new LocalFileSystem(".", null);
|
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);
|
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 interiorResources:Map<String, Dif> = new Map();
|
||||||
static var dtsResources:Map<String, DtsFile> = new Map();
|
static var dtsResources:Map<String, DtsFile> = new Map();
|
||||||
static var textureCache:Map<String, Texture> = new Map();
|
static var textureCache:Map<String, Texture> = new Map();
|
||||||
|
|
@ -24,14 +34,34 @@ class ResourceLoader {
|
||||||
|
|
||||||
// static var threadPool:FixedThreadPool = new FixedThreadPool(4);
|
// 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) {
|
public static function loadInterior(path:String) {
|
||||||
|
#if js
|
||||||
|
path = StringTools.replace(path, "data/", "");
|
||||||
|
#end
|
||||||
if (interiorResources.exists(path))
|
if (interiorResources.exists(path))
|
||||||
return interiorResources.get(path);
|
return interiorResources.get(path);
|
||||||
else {
|
else {
|
||||||
var itr:Dif;
|
var itr:Dif;
|
||||||
// var lock = new Lock();
|
// var lock = new Lock();
|
||||||
// threadPool.run(() -> {
|
// threadPool.run(() -> {
|
||||||
itr = Dif.Load(path);
|
itr = Dif.LoadFromBuffer(fileSystem.get(path).getBytes());
|
||||||
interiorResources.set(path, itr);
|
interiorResources.set(path, itr);
|
||||||
// lock.release();
|
// lock.release();
|
||||||
// });
|
// });
|
||||||
|
|
@ -41,6 +71,9 @@ class ResourceLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function loadDts(path:String) {
|
public static function loadDts(path:String) {
|
||||||
|
#if js
|
||||||
|
path = StringTools.replace(path, "data/", "");
|
||||||
|
#end
|
||||||
if (dtsResources.exists(path))
|
if (dtsResources.exists(path))
|
||||||
return dtsResources.get(path);
|
return dtsResources.get(path);
|
||||||
else {
|
else {
|
||||||
|
|
@ -57,6 +90,9 @@ class ResourceLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTexture(path:String) {
|
public static function getTexture(path:String) {
|
||||||
|
#if js
|
||||||
|
path = StringTools.replace(path, "data/", "");
|
||||||
|
#end
|
||||||
if (textureCache.exists(path))
|
if (textureCache.exists(path))
|
||||||
return textureCache.get(path);
|
return textureCache.get(path);
|
||||||
if (fileSystem.exists(path)) {
|
if (fileSystem.exists(path)) {
|
||||||
|
|
@ -69,6 +105,9 @@ class ResourceLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getImage(path:String) {
|
public static function getImage(path:String) {
|
||||||
|
#if js
|
||||||
|
path = StringTools.replace(path, "data/", "");
|
||||||
|
#end
|
||||||
if (imageCache.exists(path))
|
if (imageCache.exists(path))
|
||||||
return imageCache.get(path);
|
return imageCache.get(path);
|
||||||
if (fileSystem.exists(path)) {
|
if (fileSystem.exists(path)) {
|
||||||
|
|
@ -80,6 +119,9 @@ class ResourceLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAudio(path:String) {
|
public static function getAudio(path:String) {
|
||||||
|
#if js
|
||||||
|
path = StringTools.replace(path, "data/", "");
|
||||||
|
#end
|
||||||
if (audioCache.exists(path))
|
if (audioCache.exists(path))
|
||||||
return audioCache.get(path);
|
return audioCache.get(path);
|
||||||
if (fileSystem.exists(path)) {
|
if (fileSystem.exists(path)) {
|
||||||
|
|
@ -90,6 +132,14 @@ class ResourceLoader {
|
||||||
return null;
|
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() {
|
public static function clearInteriorResources() {
|
||||||
interiorResources = new Map();
|
interiorResources = new Map();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ import hxd.Key;
|
||||||
import src.MarbleGame;
|
import src.MarbleGame;
|
||||||
import hxd.Window;
|
import hxd.Window;
|
||||||
import haxe.DynamicAccess;
|
import haxe.DynamicAccess;
|
||||||
|
#if hl
|
||||||
import sys.io.File;
|
import sys.io.File;
|
||||||
|
#end
|
||||||
import src.ResourceLoader;
|
import src.ResourceLoader;
|
||||||
import haxe.Json;
|
import haxe.Json;
|
||||||
|
|
||||||
|
|
@ -115,7 +117,9 @@ class Settings {
|
||||||
highscoreName: highscoreName
|
highscoreName: highscoreName
|
||||||
};
|
};
|
||||||
var json = Json.stringify(outputData);
|
var json = Json.stringify(outputData);
|
||||||
|
#if hl
|
||||||
File.saveContent("settings.json", json);
|
File.saveContent("settings.json", json);
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function load() {
|
public static function load() {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import hxd.BitmapData;
|
||||||
import h3d.shader.CubeMap;
|
import h3d.shader.CubeMap;
|
||||||
import h3d.mat.Texture;
|
import h3d.mat.Texture;
|
||||||
import haxe.io.Path;
|
import haxe.io.Path;
|
||||||
import sys.io.File;
|
|
||||||
import src.ResourceLoader;
|
import src.ResourceLoader;
|
||||||
import h3d.scene.Object;
|
import h3d.scene.Object;
|
||||||
|
|
||||||
|
|
@ -50,7 +49,7 @@ class Sky extends Object {
|
||||||
|
|
||||||
function createSkyboxCubeTextured(dmlPath:String) {
|
function createSkyboxCubeTextured(dmlPath:String) {
|
||||||
if (ResourceLoader.fileSystem.exists(dmlPath)) {
|
if (ResourceLoader.fileSystem.exists(dmlPath)) {
|
||||||
var dmlFile = File.getContent(dmlPath);
|
var dmlFile = ResourceLoader.fileSystem.get(dmlPath).getText();
|
||||||
var dmlDirectory = Path.directory(dmlPath);
|
var dmlDirectory = Path.directory(dmlPath);
|
||||||
var lines = dmlFile.split('\n').map(x -> x.toLowerCase());
|
var lines = dmlFile.split('\n').map(x -> x.toLowerCase());
|
||||||
var skyboxImages = [];
|
var skyboxImages = [];
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package dts;
|
package dts;
|
||||||
|
|
||||||
import haxe.Exception;
|
import haxe.Exception;
|
||||||
import sys.io.File;
|
|
||||||
import dif.io.BytesReader;
|
import dif.io.BytesReader;
|
||||||
import dif.math.QuatF;
|
import dif.math.QuatF;
|
||||||
import dif.math.Box3F;
|
import dif.math.Box3F;
|
||||||
import dif.math.Point3F;
|
import dif.math.Point3F;
|
||||||
|
import src.ResourceLoader;
|
||||||
|
|
||||||
@:publicFields
|
@:publicFields
|
||||||
class DtsFile {
|
class DtsFile {
|
||||||
|
|
@ -61,8 +61,8 @@ class DtsFile {
|
||||||
public function new() {}
|
public function new() {}
|
||||||
|
|
||||||
public function read(filepath:String) {
|
public function read(filepath:String) {
|
||||||
var f = File.read(filepath);
|
var f = ResourceLoader.fileSystem.get(filepath);
|
||||||
var bytes = f.readAll();
|
var bytes = f.getBytes();
|
||||||
var br = new BytesReader(bytes);
|
var br = new BytesReader(bytes);
|
||||||
|
|
||||||
fileVersion = br.readInt16();
|
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.position = new Vector(82, 358);
|
||||||
exitButton.extent = new Vector(203, 88);
|
exitButton.extent = new Vector(203, 88);
|
||||||
exitButton.pressedAction = (sender) -> {
|
exitButton.pressedAction = (sender) -> {
|
||||||
|
#if hl
|
||||||
Sys.exit(0);
|
Sys.exit(0);
|
||||||
|
#end
|
||||||
};
|
};
|
||||||
homebase.addChild(exitButton);
|
homebase.addChild(exitButton);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue