Condense type forwarders into a class for less code reuse.
Edit JSDoc definitions dos2unix(1) .gitignore, edit README slightly.
This commit is contained in:
parent
95172cf285
commit
bf646d10ba
|
@ -13,3 +13,7 @@ icon.sys
|
||||||
# Unused data
|
# Unused data
|
||||||
|
|
||||||
tmp/*
|
tmp/*
|
||||||
|
|
||||||
|
# Generated documentation
|
||||||
|
|
||||||
|
documentation/*
|
||||||
|
|
|
@ -16,7 +16,7 @@ A JavaScript library (sorta) to read PS2 icons, and their related formats.
|
||||||
* Export the icon model, with all seperate shapes included to a JavaScript Object.
|
* Export the icon model, with all seperate shapes included to a JavaScript Object.
|
||||||
* Node.js compatible (CommonJS) exporting while still being compatible with other JavaScript implementations.
|
* Node.js compatible (CommonJS) exporting while still being compatible with other JavaScript implementations.
|
||||||
* Convert a 128x128x16 BGR5A1 bitmap to a RGB5A1 format.
|
* Convert a 128x128x16 BGR5A1 bitmap to a RGB5A1 format.
|
||||||
* Convert an icon or a set of icons to glTF, with textures.
|
* Convert an icon or a set of icons to glTF 2, with textures saved as PNG.
|
||||||
|
|
||||||
## What it doesn't do
|
## What it doesn't do
|
||||||
* (Re)build save files.
|
* (Re)build save files.
|
||||||
|
@ -31,7 +31,7 @@ Because it replaced what *was* left of icondumper (1).
|
||||||
|
|
||||||
## Included files
|
## Included files
|
||||||
| File | Description |
|
| File | Description |
|
||||||
| ---- | ----------------------------------------- |
|
| ---------------- | ----------------------------------------- |
|
||||||
| icon.js | The library itself. |
|
| icon.js | The library itself. |
|
||||||
| index.js | Node.js example client. |
|
| index.js | Node.js example client. |
|
||||||
| gltf-exporter.js | Node.js client to export icons to glTF 2. |
|
| gltf-exporter.js | Node.js client to export icons to glTF 2. |
|
||||||
|
|
118
icon.js
118
icon.js
|
@ -2,9 +2,66 @@
|
||||||
//LOOKING FOR: LZARI implementation (for MAX), description of CBS compression (node zlib doesn't tackle it, even with RC4'ing the data)
|
//LOOKING FOR: LZARI implementation (for MAX), description of CBS compression (node zlib doesn't tackle it, even with RC4'ing the data)
|
||||||
ICONJS_DEBUG = false;
|
ICONJS_DEBUG = false;
|
||||||
ICONJS_STRICT = true;
|
ICONJS_STRICT = true;
|
||||||
const ICONJS_VERSION = "0.5.2";
|
|
||||||
if (typeof Window !== "undefined") {
|
/**
|
||||||
window.ICONJS_VERSION = ICONJS_VERSION;
|
* The current version of the library.
|
||||||
|
* @constant {string}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
const ICONJS_VERSION = "0.6.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of DataView to add shortcuts for datatypes that I use often.
|
||||||
|
* @augments DataView
|
||||||
|
* @constructor
|
||||||
|
* @param {ArrayBuffer} buffer ArrayBuffer to base DataView from.
|
||||||
|
* @returns {Object.<string, function>}
|
||||||
|
* @access protected
|
||||||
|
*/
|
||||||
|
class yellowDataReader extends DataView {
|
||||||
|
/** Unsigned 16-bit, Little Endian.
|
||||||
|
* @param {number} i Indice offset.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
u16le(i){return super.getUint16(i, 1)};
|
||||||
|
/** Fixed-point 16-bit, Little Endian.
|
||||||
|
* @param {number} i Indice offset.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
f16le(i){return (super.getInt16(i, 1) / 4096)};
|
||||||
|
/** Unsigned 32-bit, Little Endian.
|
||||||
|
* @param {number} i Indice offset.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
u32le(i){return super.getUint32(i, 1)};
|
||||||
|
/** Floating-point 32-bit, Little Endian.
|
||||||
|
* @param {number} i Indice offset.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
f32le(i){return super.getFloat32(i, 1)};
|
||||||
|
/** 64-bit Timestamp structure, Little Endian.
|
||||||
|
* Time returned is going to be offseted for JST (GMT+09:00).
|
||||||
|
* @param {number} i Indice offset.
|
||||||
|
* @returns {Object.<string, number>}
|
||||||
|
*/
|
||||||
|
t64le(i){return {
|
||||||
|
seconds: super.getUint8(i+1),
|
||||||
|
minutes: super.getUint8(i+2),
|
||||||
|
hours: super.getUint8(i+3),
|
||||||
|
day: super.getUint8(i+4),
|
||||||
|
month: super.getUint8(i+5),
|
||||||
|
year: super.getUint16(i+6, 1)
|
||||||
|
}};
|
||||||
|
constructor(buffer) {
|
||||||
|
super(buffer);
|
||||||
|
return {
|
||||||
|
u16le: this.u16le.bind(this),
|
||||||
|
f16le: this.f16le.bind(this),
|
||||||
|
u32le: this.u32le.bind(this),
|
||||||
|
f32le: this.f32le.bind(this),
|
||||||
|
t64le: this.t64le.bind(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,6 +89,7 @@ function setStrictness(value) {
|
||||||
* Converts a texture format to a generalized texture type character.
|
* Converts a texture format to a generalized texture type character.
|
||||||
* @param {number} input - texture format
|
* @param {number} input - texture format
|
||||||
* @returns {string} U: uncompressed, N: none, C: compressed
|
* @returns {string} U: uncompressed, N: none, C: compressed
|
||||||
|
* @access protected
|
||||||
*/
|
*/
|
||||||
function getTextureFormat(i) {
|
function getTextureFormat(i) {
|
||||||
if (i<8) {
|
if (i<8) {
|
||||||
|
@ -57,8 +115,7 @@ function uncompressTexture(texData) {
|
||||||
if (texData.length & 1) {
|
if (texData.length & 1) {
|
||||||
throw "Texture size isn't a multiple of 2 (was ${texData.length})";
|
throw "Texture size isn't a multiple of 2 (was ${texData.length})";
|
||||||
}
|
}
|
||||||
const view = new DataView(texData);
|
const {u16le} = new yellowDataReader(texData);
|
||||||
const u16le = function(i){return view.getUint16(i, 1)}
|
|
||||||
let uncompressed = new Uint16Array(16384);
|
let uncompressed = new Uint16Array(16384);
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
for (let index = 0; index < 16384;) {
|
for (let index = 0; index < 16384;) {
|
||||||
|
@ -122,6 +179,7 @@ function convertBGR5A1toRGB5A1(bgrData) {
|
||||||
* Takes a string and returns the first part before a null character.
|
* Takes a string and returns the first part before a null character.
|
||||||
* @param {string} dirty - String to slice from first null character.
|
* @param {string} dirty - String to slice from first null character.
|
||||||
* @returns {string} Substring of dirty before first null character.
|
* @returns {string} Substring of dirty before first null character.
|
||||||
|
* @access protected
|
||||||
*/
|
*/
|
||||||
function stringScrubber(dirty) {
|
function stringScrubber(dirty) {
|
||||||
return dirty.replaceAll("\x00","").substring(0, (dirty.indexOf("\x00") === -1) ? dirty.length : dirty.indexOf("\x00"));
|
return dirty.replaceAll("\x00","").substring(0, (dirty.indexOf("\x00") === -1) ? dirty.length : dirty.indexOf("\x00"));
|
||||||
|
@ -134,9 +192,7 @@ function stringScrubber(dirty) {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
function readPS2D(input) {
|
function readPS2D(input) {
|
||||||
const view = new DataView(input);
|
const {u32le, f32le} = new yellowDataReader(input);
|
||||||
const u32le = function(i){return view.getUint32(i, 1)}
|
|
||||||
const f32le = function(i){return view.getFloat32(i, 1)}
|
|
||||||
//!pattern ps2d.hexpat
|
//!pattern ps2d.hexpat
|
||||||
const header = u32le(0);
|
const header = u32le(0);
|
||||||
if (header !== 0x44325350) {
|
if (header !== 0x44325350) {
|
||||||
|
@ -203,10 +259,7 @@ function readPS2D(input) {
|
||||||
*/
|
*/
|
||||||
function readIconFile(input) {
|
function readIconFile(input) {
|
||||||
//!pattern ps2icon-hacked.hexpat
|
//!pattern ps2icon-hacked.hexpat
|
||||||
const view = new DataView(input);
|
const {u32le, f32le, f16le} = new yellowDataReader(input);
|
||||||
const u32le = function(i){return view.getUint32(i, 1)}
|
|
||||||
const f32le = function(i){return view.getFloat32(i, 1)}
|
|
||||||
const f16le = function(i){return (view.getInt16(i, 1) / 4096)}
|
|
||||||
const u32_rgba8 = function(i) {return {
|
const u32_rgba8 = function(i) {return {
|
||||||
r: (i & 0xff),
|
r: (i & 0xff),
|
||||||
g: ((i & 0xff00) >> 8),
|
g: ((i & 0xff00) >> 8),
|
||||||
|
@ -327,16 +380,7 @@ function readIconFile(input) {
|
||||||
* @access protected
|
* @access protected
|
||||||
*/
|
*/
|
||||||
function readEntryBlock(input) {
|
function readEntryBlock(input) {
|
||||||
const view = new DataView(input);
|
const {u32le, t64le} = new yellowDataReader(input);
|
||||||
const u32le = function(i){return view.getUint32(i, 1)};
|
|
||||||
const t64le = function(i){return {
|
|
||||||
seconds: view.getUint8(i+1),
|
|
||||||
minutes: view.getUint8(i+2),
|
|
||||||
hours: view.getUint8(i+3),
|
|
||||||
day: view.getUint8(i+4),
|
|
||||||
month: view.getUint8(i+5),
|
|
||||||
year: view.getUint16(i+6, 1)
|
|
||||||
}}; //NOTE: times are in JST timezone (GMT+09:00), so clients should implement correctly!
|
|
||||||
//!pattern psu_file.hexpat
|
//!pattern psu_file.hexpat
|
||||||
const permissions = u32le(0);
|
const permissions = u32le(0);
|
||||||
let type;
|
let type;
|
||||||
|
@ -419,16 +463,7 @@ function readEmsPsuFile(input){
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
function readPsvFile(input){
|
function readPsvFile(input){
|
||||||
const view = new DataView(input);
|
const {u32le, t64le} = new yellowDataReader(input);
|
||||||
const u32le = function(i){return view.getUint32(i, 1)};
|
|
||||||
const t64le = function(i){return {
|
|
||||||
seconds: view.getUint8(i+1),
|
|
||||||
minutes: view.getUint8(i+2),
|
|
||||||
hours: view.getUint8(i+3),
|
|
||||||
day: view.getUint8(i+4),
|
|
||||||
month: view.getUint8(i+5),
|
|
||||||
year: view.getUint16(i+6, 1)
|
|
||||||
}};
|
|
||||||
//!pattern psv_file.hexpat
|
//!pattern psv_file.hexpat
|
||||||
const magic = u32le(0);
|
const magic = u32le(0);
|
||||||
if (magic !== 0x50535600) {
|
if (magic !== 0x50535600) {
|
||||||
|
@ -482,16 +517,7 @@ function readPsvFile(input){
|
||||||
* @access protected
|
* @access protected
|
||||||
*/
|
*/
|
||||||
function readSxpsDescriptor(input) {
|
function readSxpsDescriptor(input) {
|
||||||
const view = new DataView(input);
|
const {u32le, t64le} = new yellowDataReader(input);
|
||||||
const u32le = function(i){return view.getUint32(i, 1)};
|
|
||||||
const t64le = function(i){return {
|
|
||||||
seconds: view.getUint8(i+1),
|
|
||||||
minutes: view.getUint8(i+2),
|
|
||||||
hours: view.getUint8(i+3),
|
|
||||||
day: view.getUint8(i+4),
|
|
||||||
month: view.getUint8(i+5),
|
|
||||||
year: view.getUint16(i+6, 1)
|
|
||||||
}}; //NOTE: times are in JST timezone (GMT+09:00), so clients should implement correctly!
|
|
||||||
//!pattern sps-xps_file.hexpat
|
//!pattern sps-xps_file.hexpat
|
||||||
//:skip 2 // ... it's the file descriptor block size (including the bytes themselves, so 250)
|
//:skip 2 // ... it's the file descriptor block size (including the bytes themselves, so 250)
|
||||||
const int_filename = input.slice(2, 66);
|
const int_filename = input.slice(2, 66);
|
||||||
|
@ -533,8 +559,7 @@ function readSxpsDescriptor(input) {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
function readSharkXPortSxpsFile(input) {
|
function readSharkXPortSxpsFile(input) {
|
||||||
const view = new DataView(input);
|
const {u32le} = new yellowDataReader(input);
|
||||||
const u32le = function(i){return view.getUint32(i, 1)};
|
|
||||||
//!pattern sps-xps_file.hexpat
|
//!pattern sps-xps_file.hexpat
|
||||||
const identLength = u32le(0);
|
const identLength = u32le(0);
|
||||||
if(identLength !== 13) {
|
if(identLength !== 13) {
|
||||||
|
@ -590,7 +615,10 @@ function readSharkXPortSxpsFile(input) {
|
||||||
return fsOut;
|
return fsOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Define (module.)exports with all public functions */
|
/**
|
||||||
|
* Define (module.)exports with all public functions
|
||||||
|
* @exports icondumper2/icon
|
||||||
|
*/
|
||||||
exports = {
|
exports = {
|
||||||
readers: {readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile},
|
readers: {readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile},
|
||||||
helpers: {uncompressTexture, convertBGR5A1toRGB5A1},
|
helpers: {uncompressTexture, convertBGR5A1toRGB5A1},
|
||||||
|
|
Loading…
Reference in New Issue