#wip: SFFModel.

This commit is contained in:
KyMAN 2026-07-20 07:23:37 +02:00
commit 9b6983e556
6 changed files with 481 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/Data
/Public/data
*.[Ss]ecrets.*
*.[Ss]ecret.*
*.[Ss]ecrets
*.[Ss]ecret
*.deleted

View File

@ -0,0 +1,65 @@
"use strict";
import {Common} from "http://localhost:18000/Public/ecma/Utils/Common.ecma.js";
import {Check} from "http://localhost:18000/Public/ecma/Utils/Checks.ecma.js";
/**
* @class BinaryStructureModel
* @constructor
* @param {!string} type
* @param {!string} name
* @param {!number} from
* @param {?number} size
* @param {!Array.<BinaryStructureModel>} [childs = []]
* @returns {void}
* @access public
* @static
*/
export const BinaryStructureModel = (function(){
/**
* @constructs BinaryStructureModel
* @param {!string} type
* @param {!string} name
* @param {!number} from
* @param {?number} size
* @param {!Array.<BinaryStructureModel>} [childs = []]
* @returns {void}
* @access private
* @static
*/
const BinaryStructureModel = function(type, name, from, size, childs = []){
/** @type {BinaryStructureModel} */
const self = this;
/** @type {string} */
this.name = name;
/** @type {string} */
this.type = type;
/** @type {number} */
this.from = from;
/** @type {number|null} */
this.size = null;
/** @type {Array.<BinaryStructureModel>} */
this.childs = childs || [];
/** @type {number|null} */
this.next = null;
/**
* @returns {void}
* @access private
*/
const constructor = () => {
if(Check.is_array(size))
self.next = size[0];
else if(Check.is_number(size))
self.size = size;
};
constructor();
};
return BinaryStructureModel;
})();

View File

@ -0,0 +1,177 @@
"use strict";
import {Common} from "http://localhost:18000/Public/ecma/Utils/Common.ecma.js";
import {Tools} from "../Utils/Tools.ecma.js";
export const SFFPCXModel = (function(){
const SFFPCXModel = function(pcx, palette_size = 0){
const self = this;
this.id = null;
this.version = null;
this.encoding = null;
this.bit = null;
this.x = null;
this.y = null;
this.width = null;
this.height = null;
this.horizontal = null;
this.vertical = null;
this.color_map = null;
this.reserved = null;
this.planes = null;
this.bytes_per_line = null;
this.information = null;
this.filler = null;
this.data = null;
this.ok = null;
this.palette = null;
const constructor = () => {
Object.entries(Tools.load_binary(pcx, [
["id", "integer", 1],
["version", "integer", 1],
["encoding", "integer", 1],
["bit", "integer", 1],
["x", "integer", 2],
["y", "integer", 2],
["width", "integer", 2],
["height", "integer", 2],
["horizontal", "integer", 2],
["vertical", "integer", 2],
["color_map", (bytes, _) => bytes.reduce((map, byte, j) => {
const k = j / 3 >> 0;
(map[k] || (map[k] = []))[j % 3] = byte;
return map;
}), 48],
["reserved", "integer", 1],
["planes", "integer", 1],
["bytes_per_line", "integer", 2],
["information", "integer", 2],
["filler", "bytes", 58],
["data", "bytes", object => pcx.length - 128 - palette_size - (palette_size ? 1 : 0)],
["ok", (bytes, _) => !bytes.length || bytes[0] == 12, palette_size ? 1 : 0],
["palette", "bytes", palette_size]
])).forEach(([key, value]) => {
self[key] = value;
});
};
constructor();
};
return SFFPCXModel;
})();
export const SFFv1ImageModel = (function(){
const SFFv1ImageModel = function(version, data){
const self = this;
this.next = null;
this.length = null;
this.x = null;
this.y = null;
this.group = null;
this.i = null;
this.shared_flag = null;
this.comments = null;
this.image = null;
const constructor = () => {
Object.entries(Tools.load_binary(data, [
["next", "integer", 4],
["length", "integer", 4],
["x", "integer", 2],
["y", "integer", 2],
["group", "integer", 2],
["i", "integer", 2],
["shared_flag", "boolean", 1],
["comments", "string", 15],
["image", "bytes", object => object.length]
])).forEach(([key, value]) => {
if(key == "image"){
self[key] = new SFFPCXModel(value, self.shared_flag ? 0 : 768);
}else
self[key] = value;
});
};
constructor();
};
return SFFv1ImageModel;
})();
export const SFFv1Model = (function(){
const SFFv1Model = function(sff, callback = null){
const self = this;
this.signature = "";
this.version = [];
this.groups = {};
this.images = [];
this.next = null;
this.offset_palette = null;
this.shared_palette = null;
this.comments = "";
const constructor = () => {
let images_l,
groups_l,
offset;
Object.entries(Tools.load_binary(sff, [
["signature", "string", 12],
["version", "bytes", 4],
["groups", "integer", 4],
["images", "integer", 4],
["next", "integer", 4],
["offset_palette", "integer", 4],
["shared_palette", "boolean", 4],
["comments", "string", object => object.next - 36]
])).forEach(([key, value]) => {
if(key == "images")
images_l = value;
else if(key == "groups")
groups_l = value;
else
self[key] = value;
});
offset = self.next;
while(self.images.length < images_l){
const image = new SFFImageModel(self.version, sff.slice(offset));
self.images.push(image);
offset = image.next;
};
Common.execute(callback, self);
};
constructor();
};
return SFFv1Model;
})();

View File

@ -0,0 +1,153 @@
"use strict";
import {Common} from "http://localhost:18000/Public/ecma/Utils/Common.ecma.js";
import {Tools} from "../Utils/Tools.ecma.js";
export const SFFv2PaletteModel = (function(){
const SFFv2PaletteModel = function(data, sff){
const self = this;
this.group = null;
this.item = null;
this.columns = null;
this.link = null;
this.data_offset = null;
this.data_length = null;
this.palette = null;
const constructor = () => {
Object.entries(Tools.load_binary(data, [
["group", "integer", 2],
["item", "integer", 2],
["columns", "integer", 2],
["link", "integer", 2],
["data_offset", "integer", 4],
["data_length", "integer", 4]
])).forEach(([key, value]) => {
self[key] = value;
});
if(self.data_length > 0)
self.palette = sff.slice(self.data_offset, self.data_offset + self.data_length);
};
constructor();
};
return SFFv2PaletteModel;
})();
export const SFFv2SpriteModel = (function(){
const SFFv2SpriteModel = function(data, sff){
const self = this;
this.group = null;
this.item = null;
this.width = null;
this.height = null;
this.x = null;
this.y = null;
this.link = null;
this.format = null;
this.depth = null;
this.data_offset = null;
this.data_length = null;
this.palette_index = null;
this.flags = null;
this.data = null;
const constructor = () => {
Object.entries(Tools.load_binary(data, [
["group", "integer", 2],
["item", "integer", 2],
["width", "integer", 2],
["height", "integer", 2],
["x", "integer", 2],
["y", "integer", 2],
["link", "integer", 2],
["format", "integer", 1],
["depth", "integer", 1],
["data_offset", "integer", 4],
["data_length", "integer", 4],
["palette_index", "integer", 2],
["flags", "integer", 2]
])).forEach(([key, value]) => {
self[key] = value;
});
if(self.data_length > 0)
self.data = sff.slice(self.data_offset, self.data_offset + self.data_length);
};
constructor();
};
return SFFv2SpriteModel;
})();
export const SFFv2Model = (function(){
const SFFv2Model = function(sff, callback = null){
const self = this;
this.signature = "";
this.version = [];
this.sprites = [];
this.palettes = [];
this.sprite_offset = null;
this.sprite_count = null;
this.palette_offset = null;
this.palette_count = null;
const constructor = () => {
Object.entries(Tools.load_binary(sff.slice(0, 512), [
["signature", "string", 12],
["version", "bytes", 4],
["reserved1", "bytes", 4],
["reserved2", "bytes", 4],
["compat_ver", "bytes", 4],
["reserved3", "bytes", 4],
["sprite_offset", "integer", 4],
["sprite_count", "integer", 4],
["palette_offset", "integer", 4],
["palette_count", "integer", 4],
["literal_data_offset", "integer", 4],
["literal_data_length", "integer", 4],
["translated_data_offset", "integer", 4],
["translated_data_length", "integer", 4],
["filler", "bytes", 448]
])).forEach(([key, value]) => {
if(self[key] !== undefined)
self[key] = value;
});
while(self.sprite_offset < self.sprite_count){
self.sprites.push(new SFFv2SpriteModel(sff.slice(self.sprite_offset, self.sprite_offset + 16), sff));
self.sprite_offset += 16;
};
while(self.palette_offset < self.palette_count){
self.palettes.push(new SFFv2PaletteModel(sff.slice(self.palette_offset, self.palette_offset + 28), sff));
self.palette_offset += 28;
};
Common.call(callback, self);
};
constructor();
};
return SFFv2Model;
})();

View File

@ -0,0 +1,79 @@
"use strict";
import {BinaryStructureModel} from "../Models/BinaryStructureModel.ecma.js";
import {Check} from "http://localhost:18000/Public/ecma/Utils/Checks.ecma.js";
/**
* @class Tools
* @constructor
* @returns {void}
* @access public
* @static
*/
export const Tools = (function(){
/**
* @callback load_binary_type_callback
* @param {!Uint8Array|Array.<number>} data
* @param {!Object.<string, any|null>} object
* @returns {any|null}
*/
/**
* @callback load_binary_size_callback
* @param {!Object.<string, any|null>} object
* @returns {any|null}
*/
/**
* @constructs Tools
* @returns {void}
* @access private
* @static
*/
const Tools = function(){};
/**
* @param {!Uint8Array|Array.<number>} data
* @param {!Array.<Array.<[string, string|load_binary_type_callback, number|load_binary_size_callback]>>} structure
* @returns {Object.<string, any|null>}
* @access public
* @static
*/
Tools.load_binary = (data, structure) => {
/** @type {number} */
let i = 0;
return structure.reduce((object, [name, type, size]) => {
if(Check.is_function(size))
size = size(object);
if(Check.is_function(type))
object[name] = type(data.slice(i, i + size), object);
else{
/** @type {any} */
const value = data.slice(i, i + size);
object[name] = (
type == "string" ? String.fromCharCode(...value).replace(/\0/g, "") :
type == "integer" ? value.reduce((accurate, byte, j) => accurate | (byte << j * 8), 0) :
type == "boolean" ? !!value.reduce((accurate, byte, j) => accurate | (byte << j * 8), 0) :
type == "bytes" ? Array.from(value) :
value);
if(type == "integer" && size < 4 && object[name] >> size * 8 - 1)
object[name] -= 1 << size * 8;
};
i += size;
return object;
}, {});
};
return Tools;
})();

0
Public/index.html Normal file
View File