Compare commits
No commits in common. "f05ad6fac4407095f4048148fe8b75c8b79a298f" and "1b5f483df86ed9fe015cab40f8d35278e704bac2" have entirely different histories.
f05ad6fac4
...
1b5f483df8
|
@ -14,7 +14,7 @@ jobs:
|
||||||
- name: Install node.js
|
- name: Install node.js
|
||||||
uses: actions/setup-node@main
|
uses: actions/setup-node@main
|
||||||
- name: Compile documentation with JSDoc
|
- name: Compile documentation with JSDoc
|
||||||
run: npx jsdoc ./icon.js ./lzari.js -d ./documentation -R ./README.md
|
run: npx jsdoc ./icon.js -d ./documentation -R ./README.md
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: actions/upload-artifact@main
|
uses: actions/upload-artifact@main
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -13,8 +13,6 @@ icon.sys
|
||||||
*.p2m
|
*.p2m
|
||||||
*.md
|
*.md
|
||||||
*.cbs
|
*.cbs
|
||||||
*.max
|
|
||||||
*.pws
|
|
||||||
|
|
||||||
# Unused data
|
# Unused data
|
||||||
|
|
||||||
|
|
16
README.md
16
README.md
|
@ -6,11 +6,9 @@ A set of vertices with may or may not include a texture while defining colours f
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
Current implementations had some issues with rendering some icons. These were mostly:
|
Current implementations had some issues with rendering some icons. These were mostly:
|
||||||
* Not rendering any color for texture types 0-3.
|
* Not rendering any color for texture type 3.
|
||||||
* Failing to decompress some specific RLE-compressed icons. (types with bit 4 enabled)
|
* Failing to decompress some specific RLE-compressed icons. (types above 8)
|
||||||
* Requires writing/reading a specific format for successful output of data.
|
* Requires writing/reading a specific format for successful output of data.
|
||||||
* Incorrect analysis of texture types leading to assuming texture type 1 isn't the same group as texture type 3.
|
|
||||||
* Further incorrect analysis revealing it's that the texture type is a bitmask.
|
|
||||||
|
|
||||||
As of writing, there was no exporter that exists for the format that exhibited one of these problems.
|
As of writing, there was no exporter that exists for the format that exhibited one of these problems.
|
||||||
|
|
||||||
|
@ -22,7 +20,6 @@ As of writing, there was no exporter that exists for the format that exhibited o
|
||||||
* SharkPort export files (.sps)
|
* SharkPort export files (.sps)
|
||||||
* X-Port export files (.xps)
|
* X-Port export files (.xps)
|
||||||
* CodeBreaker Save export files (.cbs)
|
* CodeBreaker Save export files (.cbs)
|
||||||
* Max Drive "PowerSave"/export files (.max)
|
|
||||||
* PS2 icons (.ico, .icn)
|
* PS2 icons (.ico, .icn)
|
||||||
* PS2D format (icon.sys)
|
* PS2D format (icon.sys)
|
||||||
|
|
||||||
|
@ -33,7 +30,6 @@ As of writing, there was no exporter that exists for the format that exhibited o
|
||||||
* CommonJS (that includes node!) module exporting while still being compatible with other JavaScript implementations.
|
* CommonJS (that includes node!) module exporting while still being compatible with other JavaScript implementations.
|
||||||
* Convert a 128x128x16 BGR5A1 bitmap to a standard RGB5A1 format.
|
* Convert a 128x128x16 BGR5A1 bitmap to a standard RGB5A1 format.
|
||||||
* Convert an icon or a set of icons to glTF 2, with textures saved as PNG.
|
* Convert an icon or a set of icons to glTF 2, with textures saved as PNG.
|
||||||
* Decompress any LZARI-formatted data.
|
|
||||||
|
|
||||||
## What it doesn't do:
|
## What it doesn't do:
|
||||||
* Create, manipulate or otherwise taint save files.
|
* Create, manipulate or otherwise taint save files.
|
||||||
|
@ -41,9 +37,9 @@ As of writing, there was no exporter that exists for the format that exhibited o
|
||||||
* Use any implementation-specific features.
|
* Use any implementation-specific features.
|
||||||
|
|
||||||
## Client compatibility:
|
## Client compatibility:
|
||||||
The library currently requires use of `const`, `let` and `class` declarations, template literals, and destructuring assignment for variables.
|
The library requires use of `const`, `let` and `class` declarations.
|
||||||
|
|
||||||
Any JavaScript implementation should work if they support all of the required features.
|
Any JavaScript implementation should work if they support all three of these declarations.
|
||||||
|
|
||||||
### Tested clients:
|
### Tested clients:
|
||||||
* Chrome (or Blink-based browser) 49 (or higher) - HTML reference client
|
* Chrome (or Blink-based browser) 49 (or higher) - HTML reference client
|
||||||
|
@ -55,13 +51,11 @@ 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. |
|
||||||
| index.htm | HTML reference client. |
|
| index.htm | HTML reference client. |
|
||||||
| lzari.js | A LZARI decompression-only library. |
|
|
||||||
| tests/iconwriter.js | Node.js. Creates icons with texture types 0-31. |
|
|
||||||
|
|
||||||
## Included example files:
|
## Included example files:
|
||||||
| Directory | Description | Formats |
|
| Directory | Description | Formats |
|
||||||
|
|
|
@ -99,22 +99,13 @@ function imf2gltf(icon = null, filename = "untitled") {
|
||||||
}]; // no indices because who needs indexing when you're transcoding?
|
}]; // no indices because who needs indexing when you're transcoding?
|
||||||
gltfOutput.materials = [{
|
gltfOutput.materials = [{
|
||||||
"name": `Material (${filename}#${index})`,
|
"name": `Material (${filename}#${index})`,
|
||||||
"pbrMetallicRoughness": null,
|
"pbrMetallicRoughness": {
|
||||||
|
"baseColorTexture": {"index":0, "texCoord": 0}
|
||||||
|
},
|
||||||
"extensions": { // or we get annoying PBR and specular stuff we don't need
|
"extensions": { // or we get annoying PBR and specular stuff we don't need
|
||||||
"KHR_materials_unlit": {}
|
"KHR_materials_unlit": {}
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
if(icon.textureFormat !== "N") {
|
|
||||||
gltfOutput.materials[0].pbrMetallicRoughness = {
|
|
||||||
"baseColorTexture": {"index":0, "texCoord": 0}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
gltfOutput.materials[0].pbrMetallicRoughness = {
|
|
||||||
"baseColorFactor": [1.0, 1.0, 1.0, 1.0],
|
|
||||||
"metallicFactor": 0.0,
|
|
||||||
"roughnessFactor": 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gltfOutput.buffers = [{"uri": `${filename}.bin`, "byteLength": outputFloatArray.byteLength}];
|
gltfOutput.buffers = [{"uri": `${filename}.bin`, "byteLength": outputFloatArray.byteLength}];
|
||||||
gltfOutput.bufferViews = [
|
gltfOutput.bufferViews = [
|
||||||
{
|
{
|
||||||
|
@ -182,14 +173,16 @@ function imf2gltf(icon = null, filename = "untitled") {
|
||||||
];
|
];
|
||||||
gltfOutput.asset = {"version": "2.0", "generator": `icondumper2/${icondumper2.version}`}
|
gltfOutput.asset = {"version": "2.0", "generator": `icondumper2/${icondumper2.version}`}
|
||||||
gltfOutput.extensionsUsed = ["KHR_materials_unlit"];
|
gltfOutput.extensionsUsed = ["KHR_materials_unlit"];
|
||||||
if(icon.textureFormat !== "N") {
|
|
||||||
gltfOutput.textures = [{"source": 0}];
|
gltfOutput.textures = [{"source": 0}];
|
||||||
gltfOutput.images = [{"name": `Texture (${filename}#${index})`, "uri": `${filename}.png`}]
|
gltfOutput.images = [{"name": `Texture (${filename}#${index})`, "uri": `${filename}.png`}]
|
||||||
}
|
|
||||||
gltfOutputArray[index] = (gltfOutput);
|
gltfOutputArray[index] = (gltfOutput);
|
||||||
}
|
}
|
||||||
let texture16 = null; // Uint16Array(16384)
|
let texture16 = null; // Uint16Array(16384)
|
||||||
switch(icon.textureFormat) {
|
switch(icon.textureFormat) {
|
||||||
|
case "N": {
|
||||||
|
texture16 = (new Uint16Array(16384)).fill(0xffff);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "C": {
|
case "C": {
|
||||||
texture16 = icondumper2.helpers.uncompressTexture(icon.texture.data);
|
texture16 = icondumper2.helpers.uncompressTexture(icon.texture.data);
|
||||||
break;
|
break;
|
||||||
|
@ -199,7 +192,6 @@ function imf2gltf(icon = null, filename = "untitled") {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(texture16 !== null) {
|
|
||||||
let texture24 = new Uint8Array(49983);
|
let texture24 = new Uint8Array(49983);
|
||||||
texture24.set([
|
texture24.set([
|
||||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
||||||
|
@ -251,9 +243,6 @@ function imf2gltf(icon = null, filename = "untitled") {
|
||||||
0xae, 0x42, 0x60, 0x82
|
0xae, 0x42, 0x60, 0x82
|
||||||
], textureOffset);
|
], textureOffset);
|
||||||
return {objects: gltfOutputArray, buffer: outputFloatArray, texture: texture24};
|
return {objects: gltfOutputArray, buffer: outputFloatArray, texture: texture24};
|
||||||
} else {
|
|
||||||
return {objects: gltfOutputArray, buffer: outputFloatArray, texture: null};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAndConvertIcon(inputData, attemptedFilename = "-") {
|
function loadAndConvertIcon(inputData, attemptedFilename = "-") {
|
||||||
|
@ -268,11 +257,10 @@ function loadAndConvertIcon(inputData, attemptedFilename = "-") {
|
||||||
}
|
}
|
||||||
(require("fs")).writeFileSync(`${filename}.bin`, glTF_output.buffer);
|
(require("fs")).writeFileSync(`${filename}.bin`, glTF_output.buffer);
|
||||||
console.info(`Saved glTF buffer as "${filename}.bin".`);
|
console.info(`Saved glTF buffer as "${filename}.bin".`);
|
||||||
if(glTF_output.texture !== null) {
|
|
||||||
(require("fs")).writeFileSync(`${filename}.png`, glTF_output.texture);
|
(require("fs")).writeFileSync(`${filename}.png`, glTF_output.texture);
|
||||||
console.info(`Saved texture as "${filename}.png".\n`);
|
console.info(`Saved texture as "${filename}.png".\n`);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// can anything de-dupe this code somehow? (index.js)
|
// can anything de-dupe this code somehow? (index.js)
|
||||||
console.info(`icon.js version ${icondumper2.version}, ${(new Date()).getFullYear().toString()} (c) yellows111`);
|
console.info(`icon.js version ${icondumper2.version}, ${(new Date()).getFullYear().toString()} (c) yellows111`);
|
||||||
|
@ -334,23 +322,6 @@ switch(processObj.argv[2]) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "max":
|
|
||||||
case "pws": {
|
|
||||||
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "file.max");
|
|
||||||
function myUnlzari(inputBuffer) {
|
|
||||||
return (require("./lzari.js").decodeLzari(inputBuffer)).buffer;
|
|
||||||
}
|
|
||||||
const parsed = iconjs.readMaxPwsFile(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength), myUnlzari);
|
|
||||||
const PS2D = iconjs.readPS2D(parsed[parsed.rootDirectory]["icon.sys"].data);
|
|
||||||
loadAndConvertIcon(iconjs.readIconFile(parsed[parsed.rootDirectory][PS2D.filenames.n].data), PS2D.filenames.n);
|
|
||||||
if(PS2D.filenames.n !== PS2D.filenames.c) {
|
|
||||||
loadAndConvertIcon(iconjs.readIconFile(parsed[parsed.rootDirectory][PS2D.filenames.c].data), PS2D.filenames.c);
|
|
||||||
}
|
|
||||||
if(PS2D.filenames.n !== PS2D.filenames.d) {
|
|
||||||
loadAndConvertIcon(iconjs.readIconFile(parsed[parsed.rootDirectory][PS2D.filenames.d].data), PS2D.filenames.d);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "sys": {
|
case "sys": {
|
||||||
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "icon.sys");
|
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "icon.sys");
|
||||||
const PS2D = iconjs.readPS2D(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
const PS2D = iconjs.readPS2D(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
||||||
|
@ -381,8 +352,6 @@ psv: Read a PS3 export file.
|
||||||
sps: Read a SharkPort export file.
|
sps: Read a SharkPort export file.
|
||||||
xps: Read a X-Port export file.
|
xps: Read a X-Port export file.
|
||||||
cbs: Read a CodeBreaker Save export file.
|
cbs: Read a CodeBreaker Save export file.
|
||||||
max: Read a Max Drive export file.
|
|
||||||
pws: Read a PowerSave export file.
|
|
||||||
|
|
||||||
sys: Read a icon.sys (964 bytes) file, and attempt
|
sys: Read a icon.sys (964 bytes) file, and attempt
|
||||||
to read icon files from the current directory.
|
to read icon files from the current directory.
|
||||||
|
|
184
icon.js
184
icon.js
|
@ -1,5 +1,5 @@
|
||||||
//To swap between mjs/esm and c6js, go to the end of this file, and (un)comment your wanted module mode.
|
//To swap between mjs/esm and c6js, go to the end of this file, and (un)comment your wanted module mode.
|
||||||
/* jshint bitwise: false, esversion: 6, -W009, -W010 */ // not doing this makes linters scream about BWOs, es6 features, and using new Primitive() instead of said primitives
|
//LOOKING FOR: LZARI implementation (for MAX and PWS files. THIS WILL COMPLETE ICONDUMPER2.)
|
||||||
var ICONJS_DEBUG = false;
|
var ICONJS_DEBUG = false;
|
||||||
var ICONJS_STRICT = true;
|
var ICONJS_STRICT = true;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ var ICONJS_STRICT = true;
|
||||||
* @constant {string}
|
* @constant {string}
|
||||||
* @default
|
* @default
|
||||||
*/
|
*/
|
||||||
const ICONJS_VERSION = "0.8.4";
|
const ICONJS_VERSION = "0.7.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The RC4 key used for ciphering CodeBreaker Saves.
|
* The RC4 key used for ciphering CodeBreaker Saves.
|
||||||
|
@ -63,22 +63,22 @@ class yellowDataReader extends DataView {
|
||||||
* @param {number} i Indice offset.
|
* @param {number} i Indice offset.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
u16le(i){return super.getUint16(i, 1);}
|
u16le(i){return super.getUint16(i, 1)};
|
||||||
/** Fixed-point 16-bit, Little Endian.
|
/** Fixed-point 16-bit, Little Endian.
|
||||||
* @param {number} i Indice offset.
|
* @param {number} i Indice offset.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
f16le(i){return (super.getInt16(i, 1) / 4096);}
|
f16le(i){return (super.getInt16(i, 1) / 4096)};
|
||||||
/** Unsigned 32-bit, Little Endian.
|
/** Unsigned 32-bit, Little Endian.
|
||||||
* @param {number} i Indice offset.
|
* @param {number} i Indice offset.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
u32le(i){return super.getUint32(i, 1);}
|
u32le(i){return super.getUint32(i, 1)};
|
||||||
/** Floating-point 32-bit, Little Endian.
|
/** Floating-point 32-bit, Little Endian.
|
||||||
* @param {number} i Indice offset.
|
* @param {number} i Indice offset.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
f32le(i){return super.getFloat32(i, 1);}
|
f32le(i){return super.getFloat32(i, 1)};
|
||||||
/** 64-bit Timestamp structure, Little Endian.
|
/** 64-bit Timestamp structure, Little Endian.
|
||||||
* Time returned is set for JST (UTC+09:00) instead of UTC.
|
* Time returned is set for JST (UTC+09:00) instead of UTC.
|
||||||
* Time returned is going to be offseted for JST (GMT+09:00).
|
* Time returned is going to be offseted for JST (GMT+09:00).
|
||||||
|
@ -92,14 +92,13 @@ class yellowDataReader extends DataView {
|
||||||
* @property {number} year - Year.
|
* @property {number} year - Year.
|
||||||
*/
|
*/
|
||||||
t64le(i){return {
|
t64le(i){return {
|
||||||
"__proto__": null,
|
|
||||||
seconds: super.getUint8(i+1),
|
seconds: super.getUint8(i+1),
|
||||||
minutes: super.getUint8(i+2),
|
minutes: super.getUint8(i+2),
|
||||||
hours: super.getUint8(i+3),
|
hours: super.getUint8(i+3),
|
||||||
day: super.getUint8(i+4),
|
day: super.getUint8(i+4),
|
||||||
month: super.getUint8(i+5),
|
month: super.getUint8(i+5),
|
||||||
year: super.getUint16(i+6, 1)
|
year: super.getUint16(i+6, 1)
|
||||||
};}
|
}};
|
||||||
constructor(buffer) {
|
constructor(buffer) {
|
||||||
super(buffer);
|
super(buffer);
|
||||||
return {
|
return {
|
||||||
|
@ -108,7 +107,7 @@ class yellowDataReader extends DataView {
|
||||||
u32le: this.u32le.bind(this),
|
u32le: this.u32le.bind(this),
|
||||||
f32le: this.f32le.bind(this),
|
f32le: this.f32le.bind(this),
|
||||||
t64le: this.t64le.bind(this)
|
t64le: this.t64le.bind(this)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +152,6 @@ function setDebug(value) {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
function setStrictness(value) {
|
function setStrictness(value) {
|
||||||
console.info("setStrictness is deprecated!");
|
|
||||||
ICONJS_STRICT = !!value;
|
ICONJS_STRICT = !!value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,18 +162,15 @@ function setStrictness(value) {
|
||||||
* @access protected
|
* @access protected
|
||||||
*/
|
*/
|
||||||
function getTextureFormat(i) {
|
function getTextureFormat(i) {
|
||||||
if(ICONJS_DEBUG) {
|
if (i<8) {
|
||||||
console.debug("Texture format: %i", i);
|
if(i==3) {
|
||||||
|
return 'N';
|
||||||
}
|
}
|
||||||
// bit 1: enable smooth shading (TODO)
|
return 'U';
|
||||||
// bit 2: ??? something weird with colours, check iconwriter.js output...
|
} else if (i>=8) {
|
||||||
if(!!(i & 4)) { // if bit 3 (textured)...
|
return 'C';
|
||||||
if(!!(i & 8)) { // if bit 4 (compressed)...
|
} else {
|
||||||
return "C"; // Compressed.
|
return void(0);
|
||||||
}
|
|
||||||
return "U"; // Uncompressed.
|
|
||||||
} else { // if bit 3 isn't set...
|
|
||||||
return "N"; // No texture.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +189,7 @@ function uncompressTexture(texData) {
|
||||||
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;) {
|
||||||
let currentValue = u16le(offset);
|
currentValue = u16le(offset);
|
||||||
if(currentValue === 0) {
|
if(currentValue === 0) {
|
||||||
// if this is specifically a katamari 1 or 2 icon, skip this byte
|
// if this is specifically a katamari 1 or 2 icon, skip this byte
|
||||||
// because it's formatted like that for some reason
|
// because it's formatted like that for some reason
|
||||||
|
@ -202,7 +197,7 @@ function uncompressTexture(texData) {
|
||||||
currentValue = u16le(offset);
|
currentValue = u16le(offset);
|
||||||
}
|
}
|
||||||
offset += 2;
|
offset += 2;
|
||||||
if (currentValue >= 0xfe00) { // everyone says this is 0xff00 but gauntlet:DL tells me its lower
|
if (currentValue >= 0xff00) {
|
||||||
//do a raw copy of the next currentValue bytes
|
//do a raw copy of the next currentValue bytes
|
||||||
let length = ((0x10000 - currentValue));
|
let length = ((0x10000 - currentValue));
|
||||||
for (let enumerator = 0; enumerator < length; enumerator++) {
|
for (let enumerator = 0; enumerator < length; enumerator++) {
|
||||||
|
@ -215,7 +210,7 @@ function uncompressTexture(texData) {
|
||||||
uncompressed[index] = u16le(offset);
|
uncompressed[index] = u16le(offset);
|
||||||
for (let indey = 0; indey < currentValue; indey++) {
|
for (let indey = 0; indey < currentValue; indey++) {
|
||||||
uncompressed[index] = u16le(offset);
|
uncompressed[index] = u16le(offset);
|
||||||
index++;
|
index++
|
||||||
}
|
}
|
||||||
offset += 2;
|
offset += 2;
|
||||||
}
|
}
|
||||||
|
@ -287,13 +282,13 @@ function readPS2D(input) {
|
||||||
{x: f32le(80), y: f32le(84), z: f32le(88)}, //:skip 4
|
{x: f32le(80), y: f32le(84), z: f32le(88)}, //:skip 4
|
||||||
{x: f32le(96), y: f32le(100), z: f32le(104)}, //:skip 4
|
{x: f32le(96), y: f32le(100), z: f32le(104)}, //:skip 4
|
||||||
{x: f32le(112), y: f32le(116), z: f32le(120)} //:skip 4
|
{x: f32le(112), y: f32le(116), z: f32le(120)} //:skip 4
|
||||||
];
|
]
|
||||||
const lightColors = [
|
const lightColors = [
|
||||||
{r: f32le(128), g: f32le(132), b: f32le(136), a: f32le(140)},
|
{r: f32le(128), g: f32le(132), b: f32le(136), a: f32le(140)},
|
||||||
{r: f32le(144), g: f32le(148), b: f32le(152), a: f32le(156)},
|
{r: f32le(144), g: f32le(148), b: f32le(152), a: f32le(156)},
|
||||||
{r: f32le(160), g: f32le(164), b: f32le(168), a: f32le(172)},
|
{r: f32le(160), g: f32le(164), b: f32le(168), a: f32le(172)},
|
||||||
{r: f32le(176), g: f32le(180), b: f32le(184), a: f32le(188)}
|
{r: f32le(176), g: f32le(180), b: f32le(184), a: f32le(188)}
|
||||||
];
|
]
|
||||||
// P2SB says color 1 is ambient, 2-4 are for 3-point cameras
|
// P2SB says color 1 is ambient, 2-4 are for 3-point cameras
|
||||||
// official HDD icon.sys files (completely different PS2ICON text-based format) also say the same.
|
// official HDD icon.sys files (completely different PS2ICON text-based format) also say the same.
|
||||||
const int_title = input.slice(0xc0, 0x100);
|
const int_title = input.slice(0xc0, 0x100);
|
||||||
|
@ -319,7 +314,7 @@ function readPS2D(input) {
|
||||||
n: stringScrubber((new TextDecoder("utf-8")).decode(int_filename_n)),
|
n: stringScrubber((new TextDecoder("utf-8")).decode(int_filename_n)),
|
||||||
c: stringScrubber((new TextDecoder("utf-8")).decode(int_filename_c)),
|
c: stringScrubber((new TextDecoder("utf-8")).decode(int_filename_c)),
|
||||||
d: stringScrubber((new TextDecoder("utf-8")).decode(int_filename_d))
|
d: stringScrubber((new TextDecoder("utf-8")).decode(int_filename_d))
|
||||||
};
|
}
|
||||||
if(ICONJS_DEBUG){
|
if(ICONJS_DEBUG){
|
||||||
console.debug({header, titleOffset, bgAlpha, bgColors, lightIndices, lightColors, title, filenames});
|
console.debug({header, titleOffset, bgAlpha, bgColors, lightIndices, lightColors, title, filenames});
|
||||||
}
|
}
|
||||||
|
@ -341,7 +336,7 @@ function readIconFile(input) {
|
||||||
b: ((i & 0xff0000) >> 16),
|
b: ((i & 0xff0000) >> 16),
|
||||||
a: (i > 0x7fffffff ? 255 : (((i & 0xff000000) >>> 24) * 2)+1)
|
a: (i > 0x7fffffff ? 255 : (((i & 0xff000000) >>> 24) * 2)+1)
|
||||||
// I don't think alpha transparency is actually USED in icons?, rendering with it looks strange.
|
// I don't think alpha transparency is actually USED in icons?, rendering with it looks strange.
|
||||||
};};
|
}};
|
||||||
const magic = u32le(0);
|
const magic = u32le(0);
|
||||||
if (magic !== 0x010000) {
|
if (magic !== 0x010000) {
|
||||||
// USER NOTICE: So far, I have yet to parse an icon that hasn't had 0x00010000 as it's magic.
|
// USER NOTICE: So far, I have yet to parse an icon that hasn't had 0x00010000 as it's magic.
|
||||||
|
@ -356,7 +351,7 @@ function readIconFile(input) {
|
||||||
const textureFormat = getTextureFormat(textureType);
|
const textureFormat = getTextureFormat(textureType);
|
||||||
//:skip 4
|
//:skip 4
|
||||||
const numberOfVertexes = u32le(16);
|
const numberOfVertexes = u32le(16);
|
||||||
if((numberOfVertexes % 3) > 0){
|
if(!!(numberOfVertexes % 3)){
|
||||||
throw `Not enough vertices to define a triangle (${numberOfVertexes % 3} vertices remained).`;
|
throw `Not enough vertices to define a triangle (${numberOfVertexes % 3} vertices remained).`;
|
||||||
}
|
}
|
||||||
// format: [xxyyzzaa * numberOfShapes][xxyyzzaa][uuvvrgba], ((8 * numberOfShapes) + 16) [per chunk]
|
// format: [xxyyzzaa * numberOfShapes][xxyyzzaa][uuvvrgba], ((8 * numberOfShapes) + 16) [per chunk]
|
||||||
|
@ -391,7 +386,7 @@ function readIconFile(input) {
|
||||||
vertices.push({shapes, normal, uv, color});
|
vertices.push({shapes, normal, uv, color});
|
||||||
}
|
}
|
||||||
offset = (20+(numberOfVertexes * chunkLength));
|
offset = (20+(numberOfVertexes * chunkLength));
|
||||||
const animationHeader = {id: u32le(offset), length: u32le(offset+4), speed: f32le(offset+8), "offset": u32le(offset+12), keyframes: u32le(offset+16)};
|
animationHeader = {id: u32le(offset), length: u32le(offset+4), speed: f32le(offset+8), "offset": u32le(offset+12), keyframes: u32le(offset+16)};
|
||||||
let animData = new Array();
|
let animData = new Array();
|
||||||
// now we have to enumerate values, so now we introduce an offset value.
|
// now we have to enumerate values, so now we introduce an offset value.
|
||||||
// format for a keyframe: sssskkkk[ffffvvvv] where [ffffvvvv] repeat based on the value that kkkk(eys) has.
|
// format for a keyframe: sssskkkk[ffffvvvv] where [ffffvvvv] repeat based on the value that kkkk(eys) has.
|
||||||
|
@ -438,7 +433,7 @@ function readIconFile(input) {
|
||||||
//output of this will be another u16[0x4000] of the decompressed texture
|
//output of this will be another u16[0x4000] of the decompressed texture
|
||||||
//after that just parse output as-if it was uncompressed.
|
//after that just parse output as-if it was uncompressed.
|
||||||
//see uncompressTexture() and convertBGR5A1toRGB5A1() for more info.
|
//see uncompressTexture() and convertBGR5A1toRGB5A1() for more info.
|
||||||
const size = u32le(offset);
|
size = u32le(offset);
|
||||||
texture = {size, data: input.slice(offset+4, offset+(4+size))};
|
texture = {size, data: input.slice(offset+4, offset+(4+size))};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,13 +488,13 @@ function readEntryBlock(input) {
|
||||||
function readEmsPsuFile(input){
|
function readEmsPsuFile(input){
|
||||||
const header = readEntryBlock(input.slice(0,0x1ff));
|
const header = readEntryBlock(input.slice(0,0x1ff));
|
||||||
if(header.size > 0x7f) {
|
if(header.size > 0x7f) {
|
||||||
throw `Directory is too large! (maximum size: ${0x7f}, was ${header.size})`;
|
throw `Directory is too large! (maximum size: ${0x7f}, was ${header.size})`
|
||||||
}
|
}
|
||||||
let fsOut = {"__proto__": null, length: header.size, rootDirectory: header.filename, timestamps: header.timestamps};
|
let fsOut = {length: header.size, rootDirectory: header.filename, timestamps: header.timestamps};
|
||||||
let output = {"__proto__": null};
|
let output = new Object();
|
||||||
let offset = 512;
|
let offset = 512;
|
||||||
for (let index = 0; index < header.size; index++) {
|
for (let index = 0; index < header.size; index++) {
|
||||||
const fdesc = readEntryBlock(input.slice(offset, offset + 512));
|
fdesc = readEntryBlock(input.slice(offset, offset + 512));
|
||||||
switch(fdesc.type) {
|
switch(fdesc.type) {
|
||||||
case "directory": {
|
case "directory": {
|
||||||
offset += 512;
|
offset += 512;
|
||||||
|
@ -571,21 +566,15 @@ function readPsvFile(input){
|
||||||
for (let index = 0; index < numberOfFiles; index++) {
|
for (let index = 0; index < numberOfFiles; index++) {
|
||||||
fileData.push(input.slice(offset,offset+0x3c));
|
fileData.push(input.slice(offset,offset+0x3c));
|
||||||
offset += 0x3c;
|
offset += 0x3c;
|
||||||
}
|
};
|
||||||
//then file data after this but we already have pointers to the files we care about
|
//then file data after this but we already have pointers to the files we care about
|
||||||
const icons = {
|
const icons = {
|
||||||
n: input.slice(nModelOffset, nModelOffset+nModelSize),
|
n: input.slice(nModelOffset, nModelOffset+nModelSize),
|
||||||
c: input.slice(cModelOffset, cModelOffset+cModelSize),
|
c: input.slice(cModelOffset, cModelOffset+cModelSize),
|
||||||
d: input.slice(dModelOffset, dModelOffset+dModelSize),
|
d: input.slice(dModelOffset, dModelOffset+dModelSize),
|
||||||
};
|
}
|
||||||
if (ICONJS_DEBUG) {
|
if (ICONJS_DEBUG) {
|
||||||
console.debug({magic, type1, type2, displayedSize,
|
console.debug({magic, type1, type2, displayedSize, ps2dOffset, ps2dSize, nModelOffset, nModelSize, cModelOffset, cModelSize, dModelOffset, dModelSize, numberOfFiles, rootDirectoryData, fileData})
|
||||||
ps2dOffset, ps2dSize,
|
|
||||||
nModelOffset, nModelSize,
|
|
||||||
cModelOffset, cModelSize,
|
|
||||||
dModelOffset, dModelSize,
|
|
||||||
numberOfFiles, rootDirectoryData, fileData
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return {icons, "icon.sys": input.slice(ps2dOffset, ps2dOffset+ps2dSize), timestamps};
|
return {icons, "icon.sys": input.slice(ps2dOffset, ps2dOffset+ps2dSize), timestamps};
|
||||||
}
|
}
|
||||||
|
@ -648,7 +637,7 @@ function readSharkXPortSxpsFile(input) {
|
||||||
let offset = 4;
|
let offset = 4;
|
||||||
const ident = input.slice(offset, offset+identLength);
|
const ident = input.slice(offset, offset+identLength);
|
||||||
if((new TextDecoder("utf-8")).decode(ident) !== "SharkPortSave") {
|
if((new TextDecoder("utf-8")).decode(ident) !== "SharkPortSave") {
|
||||||
throw `Unrecognized file identification string. Expected "SharkPortSave".`;
|
throw `Unrecognized file identification string. Expected "SharkPortSave".`
|
||||||
}
|
}
|
||||||
offset += (identLength + 4);
|
offset += (identLength + 4);
|
||||||
const titleLength = u32le(offset);
|
const titleLength = u32le(offset);
|
||||||
|
@ -668,19 +657,19 @@ function readSharkXPortSxpsFile(input) {
|
||||||
const comments = {
|
const comments = {
|
||||||
"game": stringScrubber((new TextDecoder("utf-8")).decode(title)),
|
"game": stringScrubber((new TextDecoder("utf-8")).decode(title)),
|
||||||
"name": stringScrubber((new TextDecoder("utf-8")).decode(description))
|
"name": stringScrubber((new TextDecoder("utf-8")).decode(description))
|
||||||
};
|
}
|
||||||
if(description2Length !== 0) {
|
if(description2Length !== 0) {
|
||||||
comments.desc = stringScrubber((new TextDecoder("utf-8")).decode(description2));
|
comments.desc = stringScrubber((new TextDecoder("utf-8")).decode(description2));
|
||||||
}
|
}
|
||||||
//const totalSize = u32le(offset); has data, unused in script
|
const totalSize = u32le(offset);
|
||||||
offset += 4;
|
offset += 4;
|
||||||
const header = readSxpsDescriptor(input.slice(offset, offset + 250));
|
const header = readSxpsDescriptor(input.slice(offset, offset + 250));
|
||||||
offset += 250;
|
offset += 250;
|
||||||
// alright now lets parse some actual data
|
// alright now lets parse some actual data
|
||||||
let fsOut = {"__proto__": null, length: header.size, rootDirectory: header.filename, timestamps: header.timestamps, comments};
|
let fsOut = {length: header.size, rootDirectory: header.filename, timestamps: header.timestamps, comments};
|
||||||
let output = {"__proto__": null};
|
let output = new Object();
|
||||||
for (let index = 0; index < (header.size - 2); index++) {
|
for (let index = 0; index < (header.size - 2); index++) {
|
||||||
const fdesc = readSxpsDescriptor(input.slice(offset, offset + 250));
|
fdesc = readSxpsDescriptor(input.slice(offset, offset + 250));
|
||||||
switch(fdesc.type) {
|
switch(fdesc.type) {
|
||||||
case "directory": {
|
case "directory": {
|
||||||
offset += 250;
|
offset += 250;
|
||||||
|
@ -714,7 +703,7 @@ function readSharkXPortSxpsFile(input) {
|
||||||
*/
|
*/
|
||||||
function readCodeBreakerCbsDirectory(input) {
|
function readCodeBreakerCbsDirectory(input) {
|
||||||
const {u32le, t64le} = new yellowDataReader(input);
|
const {u32le, t64le} = new yellowDataReader(input);
|
||||||
const virtualFilesystem = {"__proto__": null};
|
const virtualFilesystem = new Object();
|
||||||
for (let offset = 0; offset < input.byteLength;) {
|
for (let offset = 0; offset < input.byteLength;) {
|
||||||
const timestamps = {created: t64le(offset), modified: t64le(offset+8)};
|
const timestamps = {created: t64le(offset), modified: t64le(offset+8)};
|
||||||
const dataSize = u32le(offset+16);
|
const dataSize = u32le(offset+16);
|
||||||
|
@ -739,7 +728,7 @@ function readCodeBreakerCbsDirectory(input) {
|
||||||
*/
|
*/
|
||||||
function readCodeBreakerCbsFile(input, inflator = null) {
|
function readCodeBreakerCbsFile(input, inflator = null) {
|
||||||
if(typeof inflator !== "function") {
|
if(typeof inflator !== "function") {
|
||||||
throw "No inflator function passed. Skipping.";
|
throw `No inflator function passed. Skipping.`;
|
||||||
}
|
}
|
||||||
const {u32le, t64le} = new yellowDataReader(input);
|
const {u32le, t64le} = new yellowDataReader(input);
|
||||||
const magic = u32le(0);
|
const magic = u32le(0);
|
||||||
|
@ -765,105 +754,28 @@ function readCodeBreakerCbsFile(input, inflator = null) {
|
||||||
const compressedData = input.slice(dataOffset, dataOffset + compressedSize);
|
const compressedData = input.slice(dataOffset, dataOffset + compressedSize);
|
||||||
const decipheredData = rc4Cipher(ICONJS_CBS_RC4_KEY, new Uint8Array(compressedData));
|
const decipheredData = rc4Cipher(ICONJS_CBS_RC4_KEY, new Uint8Array(compressedData));
|
||||||
const inflatedData = inflator(decipheredData);
|
const inflatedData = inflator(decipheredData);
|
||||||
const fsOut = {"__proto__": null, rootDirectory: dirName, timestamps};
|
const fsOut = {rootDirectory: dirName, timestamps};
|
||||||
fsOut[dirName] = readCodeBreakerCbsDirectory(inflatedData);
|
fsOut[dirName] = readCodeBreakerCbsDirectory(inflatedData);
|
||||||
if(ICONJS_DEBUG) {
|
|
||||||
console.debug({magic, dataOffset, compressedSize, dirName, permissions, displayName});
|
|
||||||
}
|
|
||||||
return fsOut;
|
return fsOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a Max Drive (MAX) or PowerSave (PWS) file's directory structure.
|
|
||||||
* @param {ArrayBuffer} input - Uncompressed input
|
|
||||||
* @returns {Object} (user didn't write a description)
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
function readMaxPwsDirectory(input, directorySize) {
|
|
||||||
const {u32le} = new yellowDataReader(input);
|
|
||||||
const virtualFilesystem = {"__proto__": null};
|
|
||||||
let offset = 0;
|
|
||||||
for (let index = 0; index < directorySize; index++) {
|
|
||||||
const dataSize = u32le(offset);
|
|
||||||
const _filename = input.slice(offset+4, offset+36);
|
|
||||||
const filename = stringScrubber((new TextDecoder("utf-8")).decode(_filename));
|
|
||||||
if(filename === "") { throw `Unexpected null filename at byte ${offset+4}.`; }
|
|
||||||
offset += 36;
|
|
||||||
const data = input.slice(offset, offset+dataSize);
|
|
||||||
offset += dataSize;
|
|
||||||
if(index !== directorySize - 1) {
|
|
||||||
while((offset & 15) !== 8) {
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtualFilesystem[filename] = ({dataSize, data});
|
|
||||||
}
|
|
||||||
return virtualFilesystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a Max Drive (MAX) or PowerSave (PWS) file.
|
|
||||||
* @param {ArrayBuffer} input - MAX/PWS formatted file
|
|
||||||
* @param {function(Uint8Array): ArrayBuffer} unlzari - a function which provides a LZARI-compatible decompression function.
|
|
||||||
* @returns {Object} (user didn't write a description)
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
function readMaxPwsFile(input, unlzari) {
|
|
||||||
if(typeof unlzari !== "function") {
|
|
||||||
throw "No decompresser function passed. Skipping.";
|
|
||||||
}
|
|
||||||
const {u32le} = new yellowDataReader(input);
|
|
||||||
const ident = input.slice(0, 12);
|
|
||||||
if((new TextDecoder("utf-8")).decode(ident) !== "Ps2PowerSave") {
|
|
||||||
throw "Unrecognized file identification string. Expected \"Ps2PowerSave\".";
|
|
||||||
}
|
|
||||||
//:skip 4 (u32 checksum)
|
|
||||||
const _dirName = input.slice(0x10, 0x30);
|
|
||||||
const dirName = stringScrubber((new TextDecoder("utf-8")).decode(_dirName));
|
|
||||||
const _displayName = input.slice(0x30, 0x50);
|
|
||||||
const displayName = stringScrubber((new TextDecoder("utf-8")).decode(_displayName));
|
|
||||||
const compressedSize = u32le(0x50);
|
|
||||||
if(compressedSize !== (input.byteLength - 88)) {
|
|
||||||
console.warn(`This file says it's larger then it actually is! (Given size: ${compressedSize}, actual size: ${input.byteLength - 88})`);
|
|
||||||
}
|
|
||||||
const size = u32le(0x54);
|
|
||||||
const compressedData = input.slice(88, input.byteLength);
|
|
||||||
const uncompressedData = unlzari(new Uint8Array(compressedData)); // read above why we can't trust given size
|
|
||||||
const fsOut = {"__proto__": null, rootDirectory: dirName};
|
|
||||||
fsOut[dirName] = readMaxPwsDirectory(uncompressedData, size);
|
|
||||||
// there's no... timestamps or permissions... this doesn't bode well.
|
|
||||||
if(ICONJS_DEBUG) {
|
|
||||||
console.debug({ident, compressedSize, dirName, displayName});
|
|
||||||
}
|
|
||||||
return fsOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define (module.)exports with all public functions.
|
* Define (module.)exports with all public functions.
|
||||||
* @exports icondumper2/icon
|
* @exports icondumper2/icon
|
||||||
*/ // start c6js#
|
*/ // start c6js
|
||||||
/* globals exports: true */
|
|
||||||
if(typeof exports !== "object") {
|
|
||||||
exports = {
|
exports = {
|
||||||
readers: {"readIconFile": readIconFile, "readPS2D": readPS2D, "readEmsPsuFile": readEmsPsuFile, "readPsvFile": readPsvFile, "readSharkXPortSxpsFile": readSharkXPortSxpsFile, "readCodeBreakerCbsFile": readCodeBreakerCbsFile, "readMaxPwsFile": readMaxPwsFile},
|
readers: {readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile, readCodeBreakerCbsFile},
|
||||||
helpers: {"uncompressTexture": uncompressTexture, "convertBGR5A1toRGB5A1": convertBGR5A1toRGB5A1},
|
helpers: {uncompressTexture, convertBGR5A1toRGB5A1},
|
||||||
options: {"setDebug": setDebug, "setStrictness": setStrictness},
|
options: {setDebug, setStrictness},
|
||||||
version: ICONJS_VERSION
|
version: ICONJS_VERSION
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
exports.readers = {"readIconFile": readIconFile, "readPS2D": readPS2D, "readEmsPsuFile": readEmsPsuFile, "readPsvFile": readPsvFile, "readSharkXPortSxpsFile": readSharkXPortSxpsFile, "readCodeBreakerCbsFile": readCodeBreakerCbsFile, "readMaxPwsFile": readMaxPwsFile};
|
|
||||||
exports.helpers = {"uncompressTexture": uncompressTexture, "convertBGR5A1toRGB5A1": convertBGR5A1toRGB5A1};
|
|
||||||
exports.options = {"setDebug": setDebug, "setStrictness": setStrictness};
|
|
||||||
exports.version = ICONJS_VERSION;
|
|
||||||
}
|
|
||||||
/* globals module: true */
|
|
||||||
if(typeof module !== "undefined") {
|
if(typeof module !== "undefined") {
|
||||||
module.exports = exports;
|
module.exports = exports;
|
||||||
}
|
}
|
||||||
//end c6js
|
//end c6js
|
||||||
//start esm
|
//start esm
|
||||||
/*export {
|
/*export {
|
||||||
readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile, readCodeBreakerCbsFile, readMaxPwsFile, uncompressTexture, convertBGR5A1toRGB5A1, setDebug, ICONJS_VERSION
|
readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile, readCodeBreakerCbsFile, uncompressTexture, convertBGR5A1toRGB5A1, setDebug, ICONJS_VERSION
|
||||||
};*/
|
};*/
|
||||||
//end esm
|
//end esm
|
205
index.htm
205
index.htm
|
@ -4,12 +4,10 @@
|
||||||
<meta charset="utf-8"></meta>
|
<meta charset="utf-8"></meta>
|
||||||
<meta name="viewport" content="initial-scale=1.5"></meta>
|
<meta name="viewport" content="initial-scale=1.5"></meta>
|
||||||
<meta name="description" content="A HTML client for icondumper2"></meta>
|
<meta name="description" content="A HTML client for icondumper2"></meta>
|
||||||
<title>icondumper2 HTML reference client</title>
|
<title>icondumper2 - HTML reference client</title>
|
||||||
<script src="icon.js"></script>
|
<script src="icon.js"></script>
|
||||||
<!-- Removing or commenting below will disable MAX reading... -->
|
<!-- If you need pako to be optional, remove/comment the bottom line. This will disable support for CBS reading, however -->
|
||||||
<script src="lzari.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pako/dist/pako_inflate.min.js" integrity="sha512-mlnC6JeOvg9V4vBpWMxGKscsCdScB6yvGVCeFF2plnQMRmwH69s9F8SHPbC0oirqfePmRBhqx2s3Bx7WIvHfWg==" crossorigin="anonymous" id="inflator-installed"></script>
|
||||||
<!-- If you need pako to be optional, remove/comment the line below. This will disable support for CBS reading, however. -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pako/dist/pako_inflate.es5.min.js" integrity="sha512-tHdgbM+jAAm3zeGYP67IjUouqHYEMuT/Wg/xvTrfEE7zsSX2GJj0G26pyobvn8Hb2LVWGp+UwsLM2HvXwCY+og==" crossorigin="anonymous"></script>
|
|
||||||
<style>
|
<style>
|
||||||
html {color: #ccc; background: black; font-family: sans-serif}
|
html {color: #ccc; background: black; font-family: sans-serif}
|
||||||
#title1, #title2 {
|
#title1, #title2 {
|
||||||
|
@ -25,31 +23,22 @@
|
||||||
#version {text-shadow: 1px 1px 2px black;}
|
#version {text-shadow: 1px 1px 2px black;}
|
||||||
a {color: #ccc;}
|
a {color: #ccc;}
|
||||||
.inputbox {
|
.inputbox {
|
||||||
display: table-cell;
|
display: inline-grid;
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
border: 1px gray solid;
|
border: 1px gray solid;
|
||||||
padding: 0.175em 0.25em 0 0.25em;
|
padding: 0.175em 0.25em 0 0.25em;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
border-right: 0;
|
|
||||||
}
|
|
||||||
.last-input {
|
|
||||||
border-right: 1px gray solid;
|
|
||||||
}
|
|
||||||
.inputbox > input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<meta data-comment="WebGL Shader: Icon">
|
<meta data-comment="WebGL Shader: Icon">
|
||||||
<script type="text/plain" id="shader-icon-v">
|
<script type="text/plain" id="shader-icon-v">
|
||||||
attribute vec3 a_position;
|
attribute vec3 a_position;
|
||||||
attribute vec3 a_nextPosition;
|
|
||||||
attribute vec3 a_normal;
|
attribute vec3 a_normal;
|
||||||
attribute vec2 a_textureCoords;
|
attribute vec2 a_textureCoords;
|
||||||
attribute vec4 a_color;
|
attribute vec4 a_color;
|
||||||
|
|
||||||
uniform float u_rotation;
|
uniform float u_rotation;
|
||||||
uniform float u_scale;
|
uniform float u_scale;
|
||||||
uniform float u_interp;
|
|
||||||
uniform highp vec3 u_ambientLight;
|
uniform highp vec3 u_ambientLight;
|
||||||
//uniform highp vec3 u_lightColorA;
|
//uniform highp vec3 u_lightColorA;
|
||||||
//uniform highp vec3 u_lightColorB;
|
//uniform highp vec3 u_lightColorB;
|
||||||
|
@ -61,12 +50,11 @@
|
||||||
void main() {
|
void main() {
|
||||||
float angle = radians(360.0) * u_rotation;
|
float angle = radians(360.0) * u_rotation;
|
||||||
vec2 pos = vec2(cos(angle), sin(angle));
|
vec2 pos = vec2(cos(angle), sin(angle));
|
||||||
vec3 lv_interp = mix(a_position, a_nextPosition, u_interp);
|
|
||||||
// x, y, z, scale (w)
|
// x, y, z, scale (w)
|
||||||
gl_Position = vec4(
|
gl_Position = vec4(
|
||||||
(lv_interp.x * pos.x) + (lv_interp.z * pos.y), //transform the x position
|
(a_position.x * pos.x) + (a_position.z * pos.y), //transform the x position
|
||||||
(-lv_interp.y) - 2.75, // invert the y position and move down -2.75, which will center the model
|
(0.0 - a_position.y) - 2.75, // invert the y position and move down -2.75, which will center the model
|
||||||
(lv_interp.x * -pos.y) + (lv_interp.z * pos.x), //transform the z position
|
(a_position.x * -pos.y) + (a_position.z * pos.x), //transform the z position
|
||||||
u_scale
|
u_scale
|
||||||
);
|
);
|
||||||
// flip it, scale it
|
// flip it, scale it
|
||||||
|
@ -137,7 +125,7 @@
|
||||||
<h1 id="title1">No File</h1>
|
<h1 id="title1">No File</h1>
|
||||||
<h1 id="title2">Loaded</h1>
|
<h1 id="title2">Loaded</h1>
|
||||||
</div>
|
</div>
|
||||||
<span>Background/icon preview (Keyboard controls: rotate: ←/→, scale: ↑/↓, step: −/=, play: </>, change icon: 1:N/2:C/3:D):</span><br>
|
<span>Background/icon preview (Keyboard controls: rotate: ←/→, scale: ↑/↓, frame-step: −/=, change icon: 1:N/2:C/3:D):</span><br>
|
||||||
<canvas id="bgcanvas" width="480" height="480"></canvas>
|
<canvas id="bgcanvas" width="480" height="480"></canvas>
|
||||||
<canvas id="iconcanvas" width="480" height="480"></canvas>
|
<canvas id="iconcanvas" width="480" height="480"></canvas>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -145,35 +133,31 @@
|
||||||
<div id="advanced">
|
<div id="advanced">
|
||||||
<hr>
|
<hr>
|
||||||
<div class="inputbox">
|
<div class="inputbox">
|
||||||
<label for="input">icon.sys:</label>
|
<label for="input">icon.sys goes here:</label>
|
||||||
<input type="file" id="input" name="input" accept=".sys" />
|
<input type="file" id="input" name="input" accept=".sys" />
|
||||||
</div>
|
</div>
|
||||||
<div class="inputbox last-input">
|
<div class="inputbox">
|
||||||
<label for="icon">raw icon file:</label>
|
<label for="icon">raw icon file goes here:</label>
|
||||||
<input type="file" id="icon" name="icon" accept=".icn, .ico" />
|
<input type="file" id="icon" name="icon" accept=".icn, .ico" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="inputbox">
|
<div class="inputbox">
|
||||||
<label for="psuinput">EMS Memory Adapter export file (.psu):</label>
|
<label for="psuinput">EMS Memory Adapter export file (.psu) goes here:</label>
|
||||||
<input type="file" id="psuinput" name="psuinput" accept=".psu" />
|
<input type="file" id="psuinput" name="psuinput" accept=".psu" />
|
||||||
</div>
|
</div>
|
||||||
<div class="inputbox">
|
<div class="inputbox">
|
||||||
<label for="psvinput">PS3 export file (.psv):</label>
|
<label for="psvinput">PS3 export file (.psv) goes here:</label>
|
||||||
<input type="file" id="psvinput" name="psvinput" accept=".psv" />
|
<input type="file" id="psvinput" name="psvinput" accept=".psv" />
|
||||||
</div>
|
</div>
|
||||||
<div class="inputbox">
|
<div class="inputbox">
|
||||||
<label for="spsinput">SharkPort/X-Port export file (.sps, .xps):</label>
|
<label for="spsinput">SharkPort/X-Port export file (.sps, .xps) goes here:</label>
|
||||||
<input type="file" id="spsinput" name="spsinput" accept=".sps, .xps" />
|
<input type="file" id="spsinput" name="spsinput" accept=".sps, .xps" />
|
||||||
</div>
|
</div>
|
||||||
<div class="inputbox">
|
<div class="inputbox">
|
||||||
<label for="cbsinput">CodeBreaker Save export file (.cbs):</label>
|
<label for="cbsinput">CodeBreaker Save export file (.cbs) goes here:</label>
|
||||||
<input type="file" id="cbsinput" name="cbsinput" accept=".cbs" />
|
<input type="file" id="cbsinput" name="cbsinput" accept=".cbs" />
|
||||||
</div>
|
</div>
|
||||||
<div class="inputbox last-input">
|
|
||||||
<label for="maxinput">Max Drive/PowerSave export file (.max):</label>
|
|
||||||
<input type="file" id="maxinput" name="maxinput" accept=".max, .pws" />
|
|
||||||
</div>
|
|
||||||
<p>
|
<p>
|
||||||
<span>Date created: </span><span id="dateCreated">--:--:-- --/--/----</span><span> UTC+09:00</span>
|
<span>Date created: </span><span id="dateCreated">--:--:-- --/--/----</span><span> UTC+09:00</span>
|
||||||
<wbr><span>–</span>
|
<wbr><span>–</span>
|
||||||
|
@ -183,12 +167,8 @@
|
||||||
<span>File comments: </span><span id="fileCommentGame">(no title)</span><span> - </span><span id="fileCommentName">(no description)</span><span> - </span><span id="fileCommentDesc">(no other text)</span>
|
<span>File comments: </span><span id="fileCommentGame">(no title)</span><span> - </span><span id="fileCommentName">(no description)</span><span> - </span><span id="fileCommentDesc">(no other text)</span>
|
||||||
</p>
|
</p>
|
||||||
<script>
|
<script>
|
||||||
// I usually don't do in-body <script>'s, but I didn't want to do an awaited onload() again
|
// I usually don't do in-body <script>'s, but I didn't want to do an await onload() again
|
||||||
const GlobalState = {"__proto__": null, rotations: 2, scale: 0, interpfactor: 0, dataLength: 0,
|
const GlobalState = {rotations: 2, dataLength: 0, uniforms: {rotation: null, scale: null}, iconState: {source: null, currentIcon: null, currentSubmodel: 0, cachedIconSys: null}, fileReader: (new FileReader)};
|
||||||
uniforms: {"__proto__": null, rotation: null, scale: null},
|
|
||||||
iconState: {"__proto__": null, source: null, currentIcon: null, currentSubmodel: 0, cachedIconSys: null},
|
|
||||||
fileReader: (new FileReader)
|
|
||||||
};
|
|
||||||
// I don't care HOW disgusting doing this is, I'm sick of pressing escape to clear these.
|
// I don't care HOW disgusting doing this is, I'm sick of pressing escape to clear these.
|
||||||
let allInputs = document.querySelectorAll('input[type="file"]');
|
let allInputs = document.querySelectorAll('input[type="file"]');
|
||||||
Array.from(allInputs).forEach(
|
Array.from(allInputs).forEach(
|
||||||
|
@ -199,15 +179,10 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
function p0in(input) { // "prefix 0 if needed"
|
function p0in(input) { // "prefix 0 if needed"
|
||||||
if(typeof input !== "string") {input = input.toString()};
|
|
||||||
return ((input.length>=2) ? input : `0${input}`);
|
return ((input.length>=2) ? input : `0${input}`);
|
||||||
};
|
};
|
||||||
function timeToString(t64leOutput) {
|
|
||||||
return `${p0in(t64leOutput.hours)}:${p0in(t64leOutput.minutes)}:${p0in(t64leOutput.seconds)} ${p0in(t64leOutput.day)}/${p0in(t64leOutput.month)}/${t64leOutput.year}`
|
|
||||||
}
|
|
||||||
// rotation stuff
|
// rotation stuff
|
||||||
var rotationDensity = 60;
|
const rotationDensity = 60;
|
||||||
var interpolationRate = 0.34;
|
|
||||||
document.body.onkeydown = function(ev) {
|
document.body.onkeydown = function(ev) {
|
||||||
if(glBgContext === null || GlobalState.iconState.currentIcon === null) {return;}
|
if(glBgContext === null || GlobalState.iconState.currentIcon === null) {return;}
|
||||||
if(typeof GlobalState.uniforms.rotation !== "undefined") {
|
if(typeof GlobalState.uniforms.rotation !== "undefined") {
|
||||||
|
@ -237,7 +212,6 @@
|
||||||
if(GlobalState.iconState.currentSubmodel < 0) {
|
if(GlobalState.iconState.currentSubmodel < 0) {
|
||||||
GlobalState.iconState.currentSubmodel = GlobalState.iconState.currentIcon.numberOfShapes - 1;
|
GlobalState.iconState.currentSubmodel = GlobalState.iconState.currentIcon.numberOfShapes - 1;
|
||||||
}
|
}
|
||||||
GlobalState.interpfactor = 1.0;
|
|
||||||
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -246,7 +220,6 @@
|
||||||
if(GlobalState.iconState.currentSubmodel > (GlobalState.iconState.currentIcon.numberOfShapes - 1)) {
|
if(GlobalState.iconState.currentSubmodel > (GlobalState.iconState.currentIcon.numberOfShapes - 1)) {
|
||||||
GlobalState.iconState.currentSubmodel = 0;
|
GlobalState.iconState.currentSubmodel = 0;
|
||||||
}
|
}
|
||||||
GlobalState.interpfactor = 0.0;
|
|
||||||
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -268,41 +241,12 @@
|
||||||
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Comma": {
|
|
||||||
GlobalState.interpfactor -= interpolationRate;
|
|
||||||
// if we're at the lowest interp, move onto previous shape
|
|
||||||
if(GlobalState.interpfactor <= 0.0) {
|
|
||||||
GlobalState.iconState.currentSubmodel--;
|
|
||||||
if(GlobalState.iconState.currentSubmodel < 0) {
|
|
||||||
GlobalState.iconState.currentSubmodel = GlobalState.iconState.currentIcon.numberOfShapes - 1;
|
|
||||||
}
|
|
||||||
GlobalState.interpfactor = 1.0;
|
|
||||||
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;;
|
|
||||||
}
|
|
||||||
case "Period": {
|
|
||||||
GlobalState.interpfactor += interpolationRate;
|
|
||||||
// if we're at the highest interp, move onto next shape
|
|
||||||
if(GlobalState.interpfactor >= 1.0) {
|
|
||||||
GlobalState.iconState.currentSubmodel++;
|
|
||||||
if(GlobalState.iconState.currentSubmodel > (GlobalState.iconState.currentIcon.numberOfShapes - 1)) {
|
|
||||||
GlobalState.iconState.currentSubmodel = 0;
|
|
||||||
}
|
|
||||||
GlobalState.interpfactor = 0.0;
|
|
||||||
renderIcon(GlobalState.iconState.currentIcon, GlobalState.iconState.cachedIconSys, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
glFgContext.uniform1f(GlobalState.uniforms.scale, GlobalState.scale);
|
glFgContext.uniform1f(GlobalState.uniforms.scale, GlobalState.scale);
|
||||||
glFgContext.uniform1f(GlobalState.uniforms.rotation, (GlobalState.rotations/rotationDensity));
|
glFgContext.uniform1f(GlobalState.uniforms.rotation, (GlobalState.rotations/rotationDensity));
|
||||||
glFgContext.uniform1f(GlobalState.uniforms.interpolation, GlobalState.interpfactor);
|
|
||||||
glFgContext.drawArrays(glFgContext.TRIANGLES, 0, GlobalState.dataLength);
|
glFgContext.drawArrays(glFgContext.TRIANGLES, 0, GlobalState.dataLength);
|
||||||
} else {return;}
|
} else {return;}
|
||||||
}
|
}
|
||||||
|
@ -357,10 +301,8 @@
|
||||||
GlobalState.rotations = 2;
|
GlobalState.rotations = 2;
|
||||||
GlobalState.iconState.currentSubmodel = 0;
|
GlobalState.iconState.currentSubmodel = 0;
|
||||||
//clear buffers
|
//clear buffers
|
||||||
if(glFgContext !== null) {
|
|
||||||
glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);
|
glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
function renderIcon(iconData, fileMetadata = null, clearData = true) {
|
function renderIcon(iconData, fileMetadata = null, clearData = true) {
|
||||||
if(fileMetadata === null) {
|
if(fileMetadata === null) {
|
||||||
fileMetadata = {
|
fileMetadata = {
|
||||||
|
@ -368,9 +310,9 @@
|
||||||
"points": [{x:0,y:0,z:0},{x:0,y:0,z:0},{x:0,y:0,z:0}],
|
"points": [{x:0,y:0,z:0},{x:0,y:0,z:0},{x:0,y:0,z:0}],
|
||||||
"colors": [
|
"colors": [
|
||||||
{r:1,g:1,b:1,a:1}, //ambient
|
{r:1,g:1,b:1,a:1}, //ambient
|
||||||
{r:1,g:0,b:0,a:1}, // point0
|
{r:1,g:0,b:0,a:1}, //p[0]
|
||||||
{r:0,g:1,b:0,a:1}, // point1
|
{r:0,g:1,b:0,a:1}, //p[1]
|
||||||
{r:0,g:0,b:1,a:1} // point2
|
{r:0,g:0,b:1,a:1} //p[2]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -399,37 +341,37 @@
|
||||||
}
|
}
|
||||||
let iconProgram = createProgram(glFgContext, iconVertexShader, iconFragmentShader);
|
let iconProgram = createProgram(glFgContext, iconVertexShader, iconFragmentShader);
|
||||||
glFgContext.useProgram(iconProgram);
|
glFgContext.useProgram(iconProgram);
|
||||||
|
if(iconData.textureFormat !== "N") {
|
||||||
var attributes = {
|
var attributes = {
|
||||||
color: glFgContext.getAttribLocation(iconProgram, "a_color"),
|
|
||||||
position: glFgContext.getAttribLocation(iconProgram, "a_position"),
|
position: glFgContext.getAttribLocation(iconProgram, "a_position"),
|
||||||
nextPosition: glFgContext.getAttribLocation(iconProgram, "a_nextPosition")
|
textureCoords: glFgContext.getAttribLocation(iconProgram, "a_textureCoords"),
|
||||||
|
color: glFgContext.getAttribLocation(iconProgram, "a_color"),
|
||||||
};
|
};
|
||||||
var uniforms = {
|
var uniforms = {
|
||||||
|
rotation: glFgContext.getUniformLocation(iconProgram, "u_rotation"),
|
||||||
ambientLighting: glFgContext.getUniformLocation(iconProgram, "u_ambientLight"),
|
ambientLighting: glFgContext.getUniformLocation(iconProgram, "u_ambientLight"),
|
||||||
scale: glFgContext.getUniformLocation(iconProgram, "u_scale"),
|
scale: glFgContext.getUniformLocation(iconProgram, "u_scale")
|
||||||
interpolation: glFgContext.getUniformLocation(iconProgram, "u_interp"),
|
}
|
||||||
rotation: glFgContext.getUniformLocation(iconProgram, "u_rotation")
|
} else {
|
||||||
|
var attributes = {
|
||||||
|
position: glFgContext.getAttribLocation(iconProgram, "a_position"),
|
||||||
|
color: glFgContext.getAttribLocation(iconProgram, "a_color"),
|
||||||
|
};
|
||||||
|
var uniforms = {
|
||||||
|
sampler: glFgContext.getUniformLocation(iconProgram, "u_sampler"),
|
||||||
|
rotation: glFgContext.getUniformLocation(iconProgram, "u_rotation"),
|
||||||
|
ambientLighting: glFgContext.getUniformLocation(iconProgram, "u_ambientLight"),
|
||||||
|
scale: glFgContext.getUniformLocation(iconProgram, "u_scale")
|
||||||
}
|
}
|
||||||
if(iconData.textureFormat !== "N") {
|
|
||||||
attributes["textureCoords"] = glFgContext.getAttribLocation(iconProgram, "a_textureCoords");
|
|
||||||
uniforms["sampler"] = glFgContext.getUniformLocation(iconProgram, "u_sampler");
|
|
||||||
}
|
}
|
||||||
//.section SETUP
|
//.section SETUP
|
||||||
let verticesArray = new Array();
|
let verticesArray = new Array();
|
||||||
let nextVerticesArray = new Array();
|
|
||||||
let colourArray = new Array();
|
let colourArray = new Array();
|
||||||
let uvArray = new Array();
|
let uvArray = new Array();
|
||||||
let nextShape = GlobalState.iconState.currentSubmodel + 1;
|
|
||||||
if(nextShape > (iconData.numberOfShapes - 1)) {
|
|
||||||
nextShape = 0;
|
|
||||||
}
|
|
||||||
iconData.vertices.forEach(function(vertexObject){
|
iconData.vertices.forEach(function(vertexObject){
|
||||||
verticesArray.push(vertexObject.shapes[GlobalState.iconState.currentSubmodel].x);
|
verticesArray.push(vertexObject.shapes[GlobalState.iconState.currentSubmodel].x);
|
||||||
verticesArray.push(vertexObject.shapes[GlobalState.iconState.currentSubmodel].y);
|
verticesArray.push(vertexObject.shapes[GlobalState.iconState.currentSubmodel].y);
|
||||||
verticesArray.push(vertexObject.shapes[GlobalState.iconState.currentSubmodel].z);
|
verticesArray.push(vertexObject.shapes[GlobalState.iconState.currentSubmodel].z);
|
||||||
nextVerticesArray.push(vertexObject.shapes[nextShape].x);
|
|
||||||
nextVerticesArray.push(vertexObject.shapes[nextShape].y);
|
|
||||||
nextVerticesArray.push(vertexObject.shapes[nextShape].z);
|
|
||||||
colourArray.push(vertexObject.color.r/255);
|
colourArray.push(vertexObject.color.r/255);
|
||||||
colourArray.push(vertexObject.color.g/255);
|
colourArray.push(vertexObject.color.g/255);
|
||||||
colourArray.push(vertexObject.color.b/255);
|
colourArray.push(vertexObject.color.b/255);
|
||||||
|
@ -437,19 +379,12 @@
|
||||||
uvArray.push(vertexObject.uv.u);
|
uvArray.push(vertexObject.uv.u);
|
||||||
uvArray.push(vertexObject.uv.v);
|
uvArray.push(vertexObject.uv.v);
|
||||||
});
|
});
|
||||||
// TODO: Might need normals too for lighting...
|
|
||||||
//.section VERTICES
|
//.section VERTICES
|
||||||
const positionBuffer = glFgContext.createBuffer();
|
const positionBuffer = glFgContext.createBuffer();
|
||||||
glFgContext.bindBuffer(glFgContext.ARRAY_BUFFER, positionBuffer);
|
glFgContext.bindBuffer(glFgContext.ARRAY_BUFFER, positionBuffer);
|
||||||
glFgContext.enableVertexAttribArray(attributes.position);
|
glFgContext.enableVertexAttribArray(attributes.position);
|
||||||
glFgContext.bufferData(glFgContext.ARRAY_BUFFER, new Float32Array(verticesArray), glFgContext.STATIC_DRAW);
|
glFgContext.bufferData(glFgContext.ARRAY_BUFFER, new Float32Array(verticesArray), glFgContext.STATIC_DRAW);
|
||||||
glFgContext.vertexAttribPointer(attributes.position, 3, glFgContext.FLOAT, false, 0, 0);
|
glFgContext.vertexAttribPointer(attributes.position, 3, glFgContext.FLOAT, false, 0, 0);
|
||||||
//.section VERTICES_2
|
|
||||||
const nextPositionBuffer = glFgContext.createBuffer();
|
|
||||||
glFgContext.bindBuffer(glFgContext.ARRAY_BUFFER, nextPositionBuffer);
|
|
||||||
glFgContext.enableVertexAttribArray(attributes.nextPosition);
|
|
||||||
glFgContext.bufferData(glFgContext.ARRAY_BUFFER, new Float32Array(nextVerticesArray), glFgContext.STATIC_DRAW);
|
|
||||||
glFgContext.vertexAttribPointer(attributes.nextPosition, 3, glFgContext.FLOAT, false, 0, 0);
|
|
||||||
//.section COLOURS
|
//.section COLOURS
|
||||||
const colorBuffer = glFgContext.createBuffer();
|
const colorBuffer = glFgContext.createBuffer();
|
||||||
glFgContext.bindBuffer(glFgContext.ARRAY_BUFFER, colorBuffer);
|
glFgContext.bindBuffer(glFgContext.ARRAY_BUFFER, colorBuffer);
|
||||||
|
@ -476,9 +411,8 @@
|
||||||
glFgContext.uniform1f(GlobalState.uniforms.rotation, GlobalState.rotations/rotationDensity);
|
glFgContext.uniform1f(GlobalState.uniforms.rotation, GlobalState.rotations/rotationDensity);
|
||||||
|
|
||||||
//.section LIGHTING
|
//.section LIGHTING
|
||||||
let colours = fileMetadata.lighting.colors[0]; // get ambient lighting colours
|
let colours = fileMetadata.lighting.colors[0];
|
||||||
//glFgContext.uniform3f(uniforms.ambientLighting, colours.r, colours.g, colours.b);
|
//glFgContext.uniform3f(uniforms.ambientLighting, colours.r, colours.g, colours.b);
|
||||||
// TODO: figure out why rendering goes all sorts of bad when we use the actual values.
|
|
||||||
glFgContext.uniform3f(uniforms.ambientLighting, 0.75, 0.75, 0.75);
|
glFgContext.uniform3f(uniforms.ambientLighting, 0.75, 0.75, 0.75);
|
||||||
|
|
||||||
//.section SCALING
|
//.section SCALING
|
||||||
|
@ -486,11 +420,6 @@
|
||||||
if(clearData){GlobalState.scale = 3.5;}
|
if(clearData){GlobalState.scale = 3.5;}
|
||||||
glFgContext.uniform1f(GlobalState.uniforms.scale, GlobalState.scale);
|
glFgContext.uniform1f(GlobalState.uniforms.scale, GlobalState.scale);
|
||||||
|
|
||||||
//.section INTERPOLATION
|
|
||||||
GlobalState.uniforms.interpolation = uniforms.interpolation;
|
|
||||||
if (clearData) {GlobalState.interpfactor = 0.0;}
|
|
||||||
glFgContext.uniform1f(GlobalState.uniforms.interpolation, GlobalState.interpfactor);
|
|
||||||
|
|
||||||
//.section WRITE
|
//.section WRITE
|
||||||
//globalize count of triangles, as well
|
//globalize count of triangles, as well
|
||||||
GlobalState.dataLength = (verticesArray.length/3);
|
GlobalState.dataLength = (verticesArray.length/3);
|
||||||
|
@ -565,8 +494,8 @@
|
||||||
let cTime = vFilesystem.timestamps.created;
|
let cTime = vFilesystem.timestamps.created;
|
||||||
let mTime = vFilesystem.timestamps.modified;
|
let mTime = vFilesystem.timestamps.modified;
|
||||||
//TODO: use Time() to align JST times to user-local timezone
|
//TODO: use Time() to align JST times to user-local timezone
|
||||||
document.getElementById("dateCreated").textContent = timeToString(cTime);
|
document.getElementById("dateCreated").textContent = `${p0in(cTime.hours.toString())}:${p0in(cTime.minutes.toString())}:${p0in(cTime.seconds.toString())} ${p0in(cTime.day.toString())}/${p0in(cTime.month.toString())}/${cTime.year}`;
|
||||||
document.getElementById("dateModified").textContent = timeToString(mTime);
|
document.getElementById("dateModified").textContent = `${p0in(mTime.hours.toString())}:${p0in(mTime.minutes.toString())}:${p0in(mTime.seconds.toString())} ${p0in(mTime.day.toString())}/${p0in(mTime.month.toString())}/${mTime.year}`;
|
||||||
console.info("model files (psu)", output2);
|
console.info("model files (psu)", output2);
|
||||||
console.info("icon.sys (psu)", output);
|
console.info("icon.sys (psu)", output);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
@ -603,8 +532,8 @@
|
||||||
let cTime = inputData.timestamps.created;
|
let cTime = inputData.timestamps.created;
|
||||||
let mTime = inputData.timestamps.modified;
|
let mTime = inputData.timestamps.modified;
|
||||||
//TODO: use Time() to align JST times to user-local timezone
|
//TODO: use Time() to align JST times to user-local timezone
|
||||||
document.getElementById("dateCreated").textContent = timeToString(cTime);
|
document.getElementById("dateCreated").textContent = `${p0in(cTime.hours.toString())}:${p0in(cTime.minutes.toString())}:${p0in(cTime.seconds.toString())} ${p0in(cTime.day.toString())}/${p0in(cTime.month.toString())}/${cTime.year}`;
|
||||||
document.getElementById("dateModified").textContent = timeToString(mTime);
|
document.getElementById("dateModified").textContent = `${p0in(mTime.hours.toString())}:${p0in(mTime.minutes.toString())}:${p0in(mTime.seconds.toString())} ${p0in(mTime.day.toString())}/${p0in(mTime.month.toString())}/${mTime.year}`;
|
||||||
console.info("model files (psv)", icons);
|
console.info("model files (psv)", icons);
|
||||||
console.info("icon.sys (psv)", output);
|
console.info("icon.sys (psv)", output);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
@ -640,8 +569,8 @@
|
||||||
let cTime = vFilesystem.timestamps.created;
|
let cTime = vFilesystem.timestamps.created;
|
||||||
let mTime = vFilesystem.timestamps.modified;
|
let mTime = vFilesystem.timestamps.modified;
|
||||||
//TODO: use Time() to align JST times to user-local timezone
|
//TODO: use Time() to align JST times to user-local timezone
|
||||||
document.getElementById("dateCreated").textContent = timeToString(cTime);
|
document.getElementById("dateCreated").textContent = `${p0in(cTime.hours.toString())}:${p0in(cTime.minutes.toString())}:${p0in(cTime.seconds.toString())} ${p0in(cTime.day.toString())}/${p0in(cTime.month.toString())}/${cTime.year}`;
|
||||||
document.getElementById("dateModified").textContent = timeToString(mTime);
|
document.getElementById("dateModified").textContent = `${p0in(mTime.hours.toString())}:${p0in(mTime.minutes.toString())}:${p0in(mTime.seconds.toString())} ${p0in(mTime.day.toString())}/${p0in(mTime.month.toString())}/${mTime.year}`;
|
||||||
document.getElementById("fileCommentGame").textContent = vFilesystem.comments.game;
|
document.getElementById("fileCommentGame").textContent = vFilesystem.comments.game;
|
||||||
document.getElementById("fileCommentName").textContent = vFilesystem.comments.name;
|
document.getElementById("fileCommentName").textContent = vFilesystem.comments.name;
|
||||||
if(vFilesystem.comments.hasOwnProperty("desc")) {
|
if(vFilesystem.comments.hasOwnProperty("desc")) {
|
||||||
|
@ -690,43 +619,8 @@
|
||||||
cTime = vFilesystem[vFilesystem.rootDirectory]["icon.sys"].timestamps.created;
|
cTime = vFilesystem[vFilesystem.rootDirectory]["icon.sys"].timestamps.created;
|
||||||
mTime = vFilesystem[vFilesystem.rootDirectory]["icon.sys"].timestamps.modified;
|
mTime = vFilesystem[vFilesystem.rootDirectory]["icon.sys"].timestamps.modified;
|
||||||
}
|
}
|
||||||
document.getElementById("dateCreated").textContent = timeToString(cTime);
|
document.getElementById("dateCreated").textContent = `${p0in(cTime.hours.toString())}:${p0in(cTime.minutes.toString())}:${p0in(cTime.seconds.toString())} ${p0in(cTime.day.toString())}/${p0in(cTime.month.toString())}/${cTime.year.toString()}`;
|
||||||
document.getElementById("dateModified").textContent = timeToString(mTime);
|
document.getElementById("dateModified").textContent = `${p0in(mTime.hours.toString())}:${p0in(mTime.minutes.toString())}:${p0in(mTime.seconds.toString())} ${p0in(mTime.day.toString())}/${p0in(mTime.month.toString())}/${mTime.year.toString()}`;
|
||||||
console.info("model files (cbs)", output2);
|
|
||||||
console.info("icon.sys (cbs)", output);
|
|
||||||
} catch(e) {
|
|
||||||
if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);}
|
|
||||||
if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);}
|
|
||||||
console.error(e);
|
|
||||||
alert(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pwsbox = document.getElementById("maxinput");
|
|
||||||
pwsbox.onchange = function(e) {
|
|
||||||
resetDisplay();
|
|
||||||
if(pwsbox.files.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function decompressor(data) {
|
|
||||||
return (decodeLzari(data)).buffer;
|
|
||||||
}
|
|
||||||
GlobalState.fileReader.readAsArrayBuffer(pwsbox.files[0]);
|
|
||||||
GlobalState.fileReader.onloadend = function() {
|
|
||||||
GlobalState.fileReader.onloadend = void(0);
|
|
||||||
try {
|
|
||||||
let vFilesystem = readMaxPwsFile(GlobalState.fileReader.result, decompressor);
|
|
||||||
let output = readPS2D(vFilesystem[vFilesystem.rootDirectory]["icon.sys"].data);
|
|
||||||
updateDisplay(output);
|
|
||||||
let output2 = new Object();
|
|
||||||
Object.keys(output.filenames).forEach(function(file) {
|
|
||||||
output2[file] = readIconFile(vFilesystem[vFilesystem.rootDirectory][output.filenames[file]].data);
|
|
||||||
});
|
|
||||||
GlobalState.iconState.cachedIconSys = output;
|
|
||||||
GlobalState.iconState.currentSubmodel = 0;
|
|
||||||
GlobalState.iconState.source = output2;
|
|
||||||
GlobalState.iconState.currentIcon = output2.n;
|
|
||||||
renderIcon(output2.n, output);
|
|
||||||
console.info("model files (cbs)", output2);
|
console.info("model files (cbs)", output2);
|
||||||
console.info("icon.sys (cbs)", output);
|
console.info("icon.sys (cbs)", output);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
@ -813,15 +707,12 @@
|
||||||
if (typeof pako === "undefined") {
|
if (typeof pako === "undefined") {
|
||||||
document.getElementById("cbsinput").disabled = true;
|
document.getElementById("cbsinput").disabled = true;
|
||||||
}
|
}
|
||||||
if (typeof decodeLzari === "undefined") {
|
//todo: More than one model shape rendering, other 2 icons (technically done? NMW though), Animation parsing, animation tweening
|
||||||
document.getElementById("maxinput").disabled = true;
|
|
||||||
}
|
|
||||||
// TODO: More than one model shape rendering, other 2 icons (technically done? NMW though), Animation parsing, animation tweening (technically too)
|
|
||||||
</script>
|
</script>
|
||||||
<span id="version">icondumper2 <a href="./documentation" id="iconjsVersion">(unknown icon.js version)</a> [C: <span id="clientVersion">Loading...</span>] — © <span id="currentYear">2023</span> yellows111</span>
|
<span id="version">icondumper2 <a href="./documentation" id="iconjsVersion">(unknown icon.js version)</a> [C: <span id="clientVersion">Loading...</span>] — © <span id="currentYear">2023</span> yellows111</span>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById("iconjsVersion").textContent = exports.version;
|
document.getElementById("iconjsVersion").textContent = exports.version;
|
||||||
document.getElementById("clientVersion").textContent = "0.8.1";
|
document.getElementById("clientVersion").textContent = "0.7.0";
|
||||||
document.getElementById("currentYear").textContent = (new Date()).getFullYear().toString();
|
document.getElementById("currentYear").textContent = (new Date()).getFullYear().toString();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
19
index.js
19
index.js
|
@ -62,22 +62,6 @@ switch(processObj.argv[2]) {
|
||||||
console.log(output);
|
console.log(output);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "max":
|
|
||||||
case "pws": {
|
|
||||||
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "file.max");
|
|
||||||
function myUnlzari(inputBuffer) {
|
|
||||||
return (require("./lzari.js").decodeLzari(inputBuffer)).buffer;
|
|
||||||
}
|
|
||||||
const parsed = iconjs.readMaxPwsFile(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength), myUnlzari);
|
|
||||||
console.log(parsed);
|
|
||||||
const PS2D = iconjs.readPS2D(parsed[parsed.rootDirectory]["icon.sys"].data);
|
|
||||||
let output = {parsed, PS2D}
|
|
||||||
Object.keys(PS2D.filenames).forEach(function(file) {
|
|
||||||
output[file] = iconjs.readIconFile(parsed[parsed.rootDirectory][PS2D.filenames[file]].data);
|
|
||||||
});
|
|
||||||
console.log(output);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "sys": {
|
case "sys": {
|
||||||
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "icon.sys");
|
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "icon.sys");
|
||||||
const metadata = iconjs.readPS2D(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
const metadata = iconjs.readPS2D(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
||||||
|
@ -86,6 +70,7 @@ switch(processObj.argv[2]) {
|
||||||
Object.keys(metadata.filenames).forEach(function(file) {
|
Object.keys(metadata.filenames).forEach(function(file) {
|
||||||
let getFile = filesystem.readFileSync(metadata.filenames[file]);
|
let getFile = filesystem.readFileSync(metadata.filenames[file]);
|
||||||
const output = iconjs.readIconFile(getFile.buffer.slice(getFile.byteOffset, getFile.byteOffset + getFile.byteLength));
|
const output = iconjs.readIconFile(getFile.buffer.slice(getFile.byteOffset, getFile.byteOffset + getFile.byteLength));
|
||||||
|
//console.log(individialIcon);
|
||||||
console.log(`contents of ${metadata.filenames[file]} (${file}):`, output, "\n");
|
console.log(`contents of ${metadata.filenames[file]} (${file}):`, output, "\n");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -106,8 +91,6 @@ psv: Read a PS3 export file.
|
||||||
sps: Read a SharkPort export file.
|
sps: Read a SharkPort export file.
|
||||||
xps: Read a X-Port export file.
|
xps: Read a X-Port export file.
|
||||||
cbs: Read a CodeBreaker Save export file.
|
cbs: Read a CodeBreaker Save export file.
|
||||||
max: Read a Max Drive export file.
|
|
||||||
pws: Read a PowerSave export file.
|
|
||||||
|
|
||||||
sys: Read a icon.sys (964 bytes) file, and attempt
|
sys: Read a icon.sys (964 bytes) file, and attempt
|
||||||
to read icon files from the current directory.
|
to read icon files from the current directory.
|
||||||
|
|
261
lzari.js
261
lzari.js
|
@ -1,261 +0,0 @@
|
||||||
// based on Luigi Auriemma's memory2memory LZARI modification. LZARI was created by Haruhiko Okumura.
|
|
||||||
// yellows111 modifications: forced all magic constants to be their actual values,
|
|
||||||
// static calculations have been squashed, some functions have been transcluded into others
|
|
||||||
|
|
||||||
/** @copyright MIT license:
|
|
||||||
* Based on a work by Haruhiko Okumura dated 1989-07-04.
|
|
||||||
* Copyright 2023 yellows111
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
**/
|
|
||||||
/* jshint bitwise: false */ // not doing this makes linters scream about BWOs
|
|
||||||
//TODO: privatize variables and document the library
|
|
||||||
|
|
||||||
var inputData = null;
|
|
||||||
var inputLocation = 0;
|
|
||||||
var low = 0;
|
|
||||||
var high = 131072;
|
|
||||||
var value = 0;
|
|
||||||
var text_buffer = new Array(4155);
|
|
||||||
var characterToSymbol = new Array(314);
|
|
||||||
var symbolToCharacter = new Array(315);
|
|
||||||
var symbolFrequency = new Array(315);
|
|
||||||
var symbolCumulative = new Array(315);
|
|
||||||
var positionCumulative = new Array(4097);
|
|
||||||
var bit_Buffer = 0;
|
|
||||||
var bit_Mask = 0;
|
|
||||||
|
|
||||||
function GetBit() {
|
|
||||||
//partial xgetc modification
|
|
||||||
if(inputLocation >= inputData.length) {return -1;}
|
|
||||||
if((bit_Mask >>= 1) === 0) {
|
|
||||||
bit_Buffer = inputData[inputLocation++];
|
|
||||||
bit_Mask = 128;
|
|
||||||
}
|
|
||||||
return +((bit_Buffer & bit_Mask) !== 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BinarySearchSym(x) {
|
|
||||||
var i = 1;
|
|
||||||
var j = 314;
|
|
||||||
while (i < j) {
|
|
||||||
var k = ((i + j) / 2)|0;
|
|
||||||
if (symbolCumulative[k] > x) {
|
|
||||||
i = k + 1;
|
|
||||||
} else {
|
|
||||||
j = k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
function BinarySearchPos(x) {
|
|
||||||
var i = 1;
|
|
||||||
var j = 4096;
|
|
||||||
while (i < j) {
|
|
||||||
var k = ((i + j) / 2)|0;
|
|
||||||
if (positionCumulative[k] > x) {
|
|
||||||
i = k + 1;
|
|
||||||
} else {
|
|
||||||
j = k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DecodeChar() {
|
|
||||||
var range = high - low;
|
|
||||||
var sym = BinarySearchSym((((value - low + 1) * symbolCumulative[0] - 1) / range)|0);
|
|
||||||
high = low + ((range * symbolCumulative[sym - 1]) / symbolCumulative[0])|0;
|
|
||||||
low += ((range * symbolCumulative[sym ]) / symbolCumulative[0])|0;
|
|
||||||
for ( ; ; ) {
|
|
||||||
if (low >= 65536) {
|
|
||||||
value -= 65536;
|
|
||||||
low -= 65536;
|
|
||||||
high -= 65536;
|
|
||||||
} else if (low >= 32768 && high <= 98304) {
|
|
||||||
value -= 32768;
|
|
||||||
low -= 32768;
|
|
||||||
high -= 32768;
|
|
||||||
} else if (high > 65536) { break; }
|
|
||||||
low += low;
|
|
||||||
high += high;
|
|
||||||
value = 2 * value + GetBit();
|
|
||||||
}
|
|
||||||
//transcluded UpdateModel
|
|
||||||
var character = symbolToCharacter[sym];
|
|
||||||
// do not remove above, will be overwritten otherwise!
|
|
||||||
var i;
|
|
||||||
|
|
||||||
if(symbolCumulative[0] >= 32767) {
|
|
||||||
var chr = 0;
|
|
||||||
for (i = 314; i > 0; i--) {
|
|
||||||
symbolCumulative[i] = chr;
|
|
||||||
chr += (symbolFrequency[i] = (symbolFrequency[i] + 1) >> 1);
|
|
||||||
}
|
|
||||||
symbolCumulative[0] = chr;
|
|
||||||
}
|
|
||||||
for(i = sym; symbolFrequency[i] === symbolFrequency[i - 1]; i--) {}
|
|
||||||
if (i < sym) {
|
|
||||||
var ch_i = symbolToCharacter[i];
|
|
||||||
var ch_sym = symbolToCharacter[sym];
|
|
||||||
symbolToCharacter[i] = ch_sym;
|
|
||||||
symbolToCharacter[sym] = ch_i;
|
|
||||||
characterToSymbol[ch_i] = sym;
|
|
||||||
characterToSymbol[ch_sym] = i;
|
|
||||||
//i would change these vars...
|
|
||||||
//but it looks so darn cool...
|
|
||||||
}
|
|
||||||
symbolFrequency[i]++;
|
|
||||||
while (--i >= 0) {
|
|
||||||
symbolCumulative[i]++;
|
|
||||||
}
|
|
||||||
//end transclusion
|
|
||||||
return character;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DecodePosition() {
|
|
||||||
var range = high - low;
|
|
||||||
var position = BinarySearchPos((((value - low + 1) * positionCumulative[0] - 1) / range)|0);
|
|
||||||
high = low + ((range * positionCumulative[position ]) / positionCumulative[0])|0;
|
|
||||||
low += ((range * positionCumulative[position + 1]) / positionCumulative[0])|0;
|
|
||||||
for ( ; ; ) {
|
|
||||||
if (low >= 65536) {
|
|
||||||
value -= 65536;
|
|
||||||
low -= 65536;
|
|
||||||
high -= 65536;
|
|
||||||
} else if (low >= 32768 && high <= 98304) {
|
|
||||||
value -= 32768;
|
|
||||||
low -= 32768;
|
|
||||||
high -= 32768;
|
|
||||||
} else if (high > 65536) { break; }
|
|
||||||
low += low;
|
|
||||||
high += high;
|
|
||||||
value = 2 * value + GetBit();
|
|
||||||
}
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decompresses LZARI-formatted data.
|
|
||||||
* @param {Uint8Array} input - source data
|
|
||||||
* @returns {Uint8Array} output - uncompressed data
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function decodeLzari(input) {
|
|
||||||
//transcluded reset function.
|
|
||||||
inputData = null;
|
|
||||||
inputLocation = 0;
|
|
||||||
low = 0;
|
|
||||||
high = 131072;
|
|
||||||
value = 0;
|
|
||||||
text_buffer = new Array(4155);
|
|
||||||
characterToSymbol = new Array(314);
|
|
||||||
symbolToCharacter = new Array(315);
|
|
||||||
symbolFrequency = new Array(315);
|
|
||||||
symbolCumulative = new Array(315);
|
|
||||||
positionCumulative = new Array(4097);
|
|
||||||
bit_Buffer = 0;
|
|
||||||
bit_Mask = 0;
|
|
||||||
//end transclusion
|
|
||||||
|
|
||||||
inputData = input;
|
|
||||||
inputLocation = 4;
|
|
||||||
|
|
||||||
var dataSize = new DataView(input.buffer).getInt32(0,1);
|
|
||||||
|
|
||||||
if (dataSize == 0) return(0);
|
|
||||||
if (dataSize < 0) return(-1);
|
|
||||||
|
|
||||||
var outputData = new Uint8Array(dataSize);
|
|
||||||
var outputLocation = 0;
|
|
||||||
|
|
||||||
//transcluded StartDecode
|
|
||||||
for (var i = 0; i < 17; i++) {
|
|
||||||
value = 2 * value + GetBit();
|
|
||||||
}
|
|
||||||
//transcluded StartModel
|
|
||||||
symbolCumulative[314] = 0;
|
|
||||||
for (var sym = 314; sym >= 1; sym--) {
|
|
||||||
var ch = sym - 1;
|
|
||||||
characterToSymbol[ch] = sym;
|
|
||||||
symbolToCharacter[sym] = ch;
|
|
||||||
symbolFrequency[sym] = 1;
|
|
||||||
symbolCumulative[sym - 1] = (symbolCumulative[sym] + symbolFrequency[sym]);
|
|
||||||
}
|
|
||||||
symbolFrequency[0] = 0;
|
|
||||||
positionCumulative[4096] = 0;
|
|
||||||
for (i = 4096; i >= 1; i--) { // redefine i
|
|
||||||
positionCumulative[i - 1] = (positionCumulative[i] + (10000 / (i + 200))|0);
|
|
||||||
}
|
|
||||||
//end transclusion
|
|
||||||
//normal Decode process
|
|
||||||
|
|
||||||
for (i = 0; i < 4036; i++) { // redefine i
|
|
||||||
text_buffer[i] = 32;
|
|
||||||
}
|
|
||||||
var r = 4036;
|
|
||||||
|
|
||||||
for (var count = 0; count < dataSize; ) {
|
|
||||||
if(inputLocation >= inputData.length) {break;}
|
|
||||||
var c = DecodeChar();
|
|
||||||
if (c < 256) {
|
|
||||||
outputData[outputLocation++] = c;
|
|
||||||
text_buffer[r++] = c;
|
|
||||||
r &= (4095);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
i = (r - DecodePosition() - 1) & 4095; // redefine i
|
|
||||||
var j = c - 253;
|
|
||||||
for (var k = 0; k < j; k++) {
|
|
||||||
c = text_buffer[(i + k) & 4095];
|
|
||||||
outputData[outputLocation++] = c;
|
|
||||||
text_buffer[r++] = c;
|
|
||||||
r &= (4095);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.debug("LZARI I/O", {inputData, outputData});
|
|
||||||
return(outputData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define (module.)exports with all public functions.
|
|
||||||
* @exports icondumper2/lzari
|
|
||||||
*/ // start c6js
|
|
||||||
/* globals exports: true */
|
|
||||||
if(typeof exports !== "object") {
|
|
||||||
exports = {
|
|
||||||
"decodeLzari": decodeLzari
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
exports.decodeLzari = decodeLzari;
|
|
||||||
}
|
|
||||||
/* globals module: true */
|
|
||||||
if(typeof module !== "undefined") {
|
|
||||||
module.exports = exports;
|
|
||||||
}
|
|
||||||
|
|
||||||
//end c6js
|
|
||||||
//start esm
|
|
||||||
/*export {
|
|
||||||
decodeLzari
|
|
||||||
};*/
|
|
||||||
//end esm
|
|
|
@ -1,369 +0,0 @@
|
||||||
// version 1.0.0 icon, 1 shape, texture type 7 (will be discarded), something 1.0f, 36 vertices (cube)
|
|
||||||
const iconHeader = new Uint8Array([
|
|
||||||
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
||||||
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
|
||||||
0x24, 0x00, 0x00, 0x00
|
|
||||||
]); // 20 bytes
|
|
||||||
|
|
||||||
// format: [xxyyzzaa][xxyyzzaa][uuvv][rgba], (8 + 8 + 4 + 4) (position.xyzw, normal.xyzw, texcoords.st, colour.rgba)
|
|
||||||
// 0x1000 = 4096 (1.0f), 0xf000 = -4096 (-1.0f)
|
|
||||||
// color is lime green (64, 255, 64, 127)
|
|
||||||
const iconData = new Uint8Array([
|
|
||||||
// 1/36, [-1, -1, -1], [0.25, 0.25] poly 1.1 z-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
// 2/36, [-1, 1, -1], [0.25, 0.75] poly 1.2 z-1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
// 3/36, [ 1, -1, -1], [0.75, 0.75] poly 1.3 z-1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
// 4/36, [ 1, 1, -1] poly 2.1 z-1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
// 5/36, [ 1, -1, -1] poly 2.2 z-1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
// 6/36, [-1, 1, -1] poly 2.3 z-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
// 7/36, [-1, -1, -1] poly 3.1 x-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
// 8/36, [-1, -1, 1] poly 3.2 x-1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
// 9/36, [-1, 1, -1] poly 3.3 x-1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//10/36, [-1, -1, -1] poly 4.1 x-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//11/36, [-1, -1, 1] poly 4.2 x-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//12/36, [-1, 1, 1] poly 4.3 x-1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//13/36, [-1, 1, -1] poly 5.1 y+1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//14/36, [-1, 1, 1] poly 5.2 y+1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//15/36, [ 1, 1, -1] poly 5.3 y+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//16/36, [ 1, 1, -1] poly 6.1 y+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//17/36, [-1, 1, 1] poly 6.2 y+1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//18/36, [ 1, 1, 1] poly 6.3 y+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//19/36, [ 1, 1, -1] poly 7.1 x+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//20/36, [ 1, 1, 1] poly 7.2 x+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//21/36, [ 1, -1, -1] poly 7.3 x+1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//22/36, [ 1, 1, 1] poly 8.1 x+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//23/36, [ 1, -1, 1] poly 8.2 x+1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//24/36, [ 1, -1, -1] poly 8.3 x+1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//25/36, [-1, -1, -1] poly 9.1 y-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//26/36, [ 1, -1, -1] poly 9.2 y-1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//27/36, [-1, -1, 1] poly 9.3 y-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//28/36, [-1, -1, 1] poly 10.1 y-1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//29/36, [ 1, -1, -1] poly 10.2 y-1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//30/36, [ 1, -1, 1] poly 10.3 y-1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//31/36, [-1, 1, 1] poly 11.1 z+1
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//32/36, [-1, -1, 1] poly 11.2 z+1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//33/36, [ 1, 1, 1] poly 11.3 z+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
|
|
||||||
//35/36, [ 1, -1, 1] poly 12.2 z+1
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//34/36, [ 1, 1, 1] poly 12.1 z+1
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x04, 0x00, 0x04, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
//36/36, [-1, -1, 1] poly 12.3 z+1
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0xf0, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x04,
|
|
||||||
0x00, 0x0c, 0x00, 0x0c, 0x40, 0xff, 0x40, 0x7f,
|
|
||||||
]); // 864 bytes
|
|
||||||
|
|
||||||
const animData = new Uint8Array([
|
|
||||||
0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00
|
|
||||||
]); // 36 bytes
|
|
||||||
|
|
||||||
const texture_ = new Uint16Array(16384);
|
|
||||||
/** for compressed textures (RLE: fill with blue) **/
|
|
||||||
texture_[0] = 0x0004;
|
|
||||||
texture_[1] = 0x0000;
|
|
||||||
texture_[2] = 0x4000;
|
|
||||||
texture_[3] = 0b1_11111_00000_00000; // 16 bytes
|
|
||||||
|
|
||||||
/** generate texture data (RAW: fill with red) **/
|
|
||||||
for (let indice = 4; indice < 16384; indice++) {
|
|
||||||
texture_[indice] = 0b1_00000_00000_11111; //A1BGR5
|
|
||||||
} // 32768 bytes
|
|
||||||
|
|
||||||
const textureData = new Uint8Array(texture_.buffer);
|
|
||||||
|
|
||||||
/** ps2d data **/
|
|
||||||
const metadataSkeleton = new Uint8Array(Array.from({...[
|
|
||||||
0x50, 0x53, 0x32, 0x44, 0x00, 0x00,
|
|
||||||
0x20, 0x00, 0x00, 0x00, // PS2D files have a max limit of 16 characters per line
|
|
||||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
|
|
||||||
// color1.rgba
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// color2.rgba
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// color3.rgba
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// color4.rgba
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// pos1.xyzw
|
|
||||||
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f,
|
|
||||||
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// pos2.xyzw
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// pos3.xyzw
|
|
||||||
0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x80, 0xbf,
|
|
||||||
0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// light1.rgba
|
|
||||||
0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
|
|
||||||
0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// light2.rgba
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// light3.rgba
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// amblight.rgba
|
|
||||||
0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
|
|
||||||
0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// title
|
|
||||||
0x82, 0x89, 0x82, 0x83, 0x82, 0x8f, 0x82, 0x8e, // icon
|
|
||||||
0x82, 0x84, 0x82, 0x95, 0x82, 0x8d, 0x82, 0x90, // dump
|
|
||||||
0x82, 0x85, 0x82, 0x92, 0x82, 0x51, 0x81, 0x40, // er2
|
|
||||||
0x82, 0x73, 0x82, 0x85, 0x82, 0x93, 0x82, 0x94, // Test
|
|
||||||
0x82, 0x73, 0x82, 0x85, 0x82, 0x98, 0x82, 0x94, // Text
|
|
||||||
0x82, 0x95, 0x82, 0x92, 0x82, 0x85, 0x81, 0x40, // ure
|
|
||||||
0x82, 0x73, 0x82, 0x99, 0x82, 0x90, 0x82, 0x85, // Type
|
|
||||||
0x81, 0x40, 0x82, 0x58, // 9
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// file - normal
|
|
||||||
0x74, 0x65, 0x78, 0x74, 0x79, 0x70, 0x65, 0x31,
|
|
||||||
0x2E, 0x69, 0x63, 0x6E, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// file - copying
|
|
||||||
0x74, 0x65, 0x78, 0x74, 0x79, 0x70, 0x65, 0x31,
|
|
||||||
0x2E, 0x69, 0x63, 0x6E, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// file - deleting
|
|
||||||
0x74, 0x65, 0x78, 0x74, 0x79, 0x70, 0x65, 0x31,
|
|
||||||
0x2E, 0x69, 0x63, 0x6E, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
//], length:964}));
|
|
||||||
], length:1024})); // psu want block size
|
|
||||||
|
|
||||||
const CombinedIconData = new Uint8Array(33792/*20+864+36+32768*/); // psu still want block size
|
|
||||||
CombinedIconData.set(iconHeader, 0);
|
|
||||||
CombinedIconData.set(iconData, 20);
|
|
||||||
CombinedIconData.set(animData, 20+864);
|
|
||||||
CombinedIconData.set(textureData, 20+864+36);
|
|
||||||
|
|
||||||
// to be honest all of this could just be a generator
|
|
||||||
/** root directory **/
|
|
||||||
const psuHeader1 = new Uint8Array(Array.from({...[
|
|
||||||
0x27, 0x84, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x25, 0x12, 0x02, 0x05, 0xd2, 0x07,
|
|
||||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x16, 0x16, 0x16, 0x16, 0xd2, 0x16,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x69, 0x64, 0x32, 0x5f, 0x74, 0x65, 0x78, 0x74,
|
|
||||||
0x65, 0x73, 0x74, 0x39
|
|
||||||
], length:512}));
|
|
||||||
|
|
||||||
/** . **/
|
|
||||||
const psuHeader2 = new Uint8Array(Array.from({...[
|
|
||||||
0x27, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x25, 0x12, 0x02, 0x05, 0xd2, 0x07,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x16, 0x16, 0x16, 0x16, 0xd2, 0x16,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x2e
|
|
||||||
], length:512}));
|
|
||||||
|
|
||||||
/** .. **/
|
|
||||||
const psuHeader3 = new Uint8Array(Array.from({...[
|
|
||||||
0x27, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x25, 0x12, 0x02, 0x05, 0xd2, 0x07,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x16, 0x16, 0x16, 0x16, 0xd2, 0x16,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x2e, 0x2e
|
|
||||||
], length:512}));
|
|
||||||
|
|
||||||
/** icon.sys **/
|
|
||||||
const psuHeader4 = new Uint8Array(Array.from({...[
|
|
||||||
0x17, 0x84, 0x00, 0x00, 0xc4, 0x03, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x25, 0x12, 0x02, 0x05, 0xd2, 0x07,
|
|
||||||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x16, 0x16, 0x16, 0x16, 0xd2, 0x16,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x69, 0x63, 0x6f, 0x6e, 0x2e, 0x73, 0x79, 0x73
|
|
||||||
], length:512}));
|
|
||||||
|
|
||||||
/** textypeX.icn **/
|
|
||||||
const psuHeader5 = new Uint8Array(Array.from({...[
|
|
||||||
0x17, 0x84, 0x00, 0x00, 0x98, 0x83, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x25, 0x12, 0x02, 0x05, 0xd2, 0x07,
|
|
||||||
0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x16, 0x16, 0x16, 0x16, 0x16, 0xd2, 0x16,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x74, 0x65, 0x78, 0x74, 0x79, 0x70, 0x65, 0x39,
|
|
||||||
0x2E, 0x69, 0x63, 0x6E
|
|
||||||
], length:512}));
|
|
||||||
|
|
||||||
/** write psus **/
|
|
||||||
for (let iconIndice = 0; iconIndice < 32; iconIndice++) { // realistically only needs to be 0-15, not 0-31.
|
|
||||||
let needsAlpha = 0;
|
|
||||||
if(iconIndice > 9) {
|
|
||||||
needsAlpha = 7; // if we're past 9, offset to start at A instead
|
|
||||||
}
|
|
||||||
const PsuFileOutput = new Uint8Array(37888);// 37 uncompressed blocks
|
|
||||||
CombinedIconData[8] = iconIndice; // set texture type
|
|
||||||
psuHeader1[75] = 0x30 + iconIndice+needsAlpha; // set folder name
|
|
||||||
psuHeader5[71] = 0x30 + iconIndice+needsAlpha; // set file name
|
|
||||||
metadataSkeleton[251] = 0x4f + iconIndice+needsAlpha; // set display name
|
|
||||||
metadataSkeleton[267] = 0x30 + iconIndice+needsAlpha; // set normal file name
|
|
||||||
metadataSkeleton[331] = 0x30 + iconIndice+needsAlpha; // set copy file name
|
|
||||||
metadataSkeleton[395] = 0x30 + iconIndice+needsAlpha; // set deleting file name
|
|
||||||
|
|
||||||
PsuFileOutput.set(psuHeader1,0);
|
|
||||||
PsuFileOutput.set(psuHeader2,512);
|
|
||||||
PsuFileOutput.set(psuHeader3,1024);
|
|
||||||
PsuFileOutput.set(psuHeader4,1536);
|
|
||||||
PsuFileOutput.set(metadataSkeleton,2048);
|
|
||||||
PsuFileOutput.set(psuHeader5,3072);
|
|
||||||
PsuFileOutput.set(CombinedIconData, 3584);
|
|
||||||
// and then 512 bytes of padding since we don't reach a block yet
|
|
||||||
if(typeof require !== "undefined") {
|
|
||||||
require("fs").writeFileSync(`${iconIndice.toString(36)}_file.psu`, PsuFileOutput);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue