fixed (while improving) offset issues with untested psv code, added more throw-outs for model parsing
This commit is contained in:
parent
d68e2f913a
commit
4d1b790a7c
74
icon.js
74
icon.js
|
@ -1,7 +1,7 @@
|
||||||
//todo: Make this a module/mjs file. C6 compatibility can stay, if needed.
|
//todo: Make this a module/mjs file. C6 compatibility can stay, if needed.
|
||||||
ICONJS_DEBUG = false;
|
ICONJS_DEBUG = false;
|
||||||
ICONJS_STRICT = true;
|
ICONJS_STRICT = true;
|
||||||
ICONJS_VERSION = "0.3.3";
|
ICONJS_VERSION = "0.3.4";
|
||||||
|
|
||||||
function setDebug(value) {
|
function setDebug(value) {
|
||||||
ICONJS_DEBUG = !!value;
|
ICONJS_DEBUG = !!value;
|
||||||
|
@ -114,11 +114,16 @@ function readIconFile(input) {
|
||||||
throw `Not a PS2 icon file (was ${magic}, expected ${0x010000})`;
|
throw `Not a PS2 icon file (was ${magic}, expected ${0x010000})`;
|
||||||
}
|
}
|
||||||
const numberOfShapes = u32le(4);
|
const numberOfShapes = u32le(4);
|
||||||
|
if(numberOfShapes > 16) {
|
||||||
|
throw `Too many defined shapes! Is this a valid file? (file reports ${numberOfShapes} shapes)`;
|
||||||
|
}
|
||||||
const textureType = u32le(8);
|
const textureType = u32le(8);
|
||||||
const textureFormat = getTextureFormat(textureType);
|
const textureFormat = getTextureFormat(textureType);
|
||||||
//:skip 4
|
//:skip 4
|
||||||
const numberOfVertexes = u32le(16);
|
const numberOfVertexes = u32le(16);
|
||||||
// now we're entering a bunch of chunks... oh baby, now it's painful.
|
if(!!(numberOfVertexes % 3)){
|
||||||
|
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]
|
||||||
let offset = 20;
|
let offset = 20;
|
||||||
let vertices = new Array();
|
let vertices = new Array();
|
||||||
|
@ -148,13 +153,12 @@ function readIconFile(input) {
|
||||||
};
|
};
|
||||||
let color = u32_rgba8(u32le(offset+(chunkLength*index)+((numberOfShapes * 8))+12));
|
let color = u32_rgba8(u32le(offset+(chunkLength*index)+((numberOfShapes * 8))+12));
|
||||||
// keep original u32le color?
|
// keep original u32le color?
|
||||||
|
|
||||||
vertices.push({shapes, normal, uv, color});
|
vertices.push({shapes, normal, uv, color});
|
||||||
}
|
}
|
||||||
offset = (20+(numberOfVertexes * chunkLength));
|
offset = (20+(numberOfVertexes * chunkLength));
|
||||||
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 do stuff dynamically
|
// 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.
|
||||||
// sssskkkk[ffffvvvv] is repeated based on animationHeader.keyframes value.
|
// sssskkkk[ffffvvvv] is repeated based on animationHeader.keyframes value.
|
||||||
offset += 20;
|
offset += 20;
|
||||||
|
@ -177,11 +181,26 @@ function readIconFile(input) {
|
||||||
case 'U': {
|
case 'U': {
|
||||||
//where every 16-bit entry is a BGR5A1 color 0b[bbbbbgggggrrrrra]
|
//where every 16-bit entry is a BGR5A1 color 0b[bbbbbgggggrrrrra]
|
||||||
texture = new Uint16Array(input.slice(offset, (offset+0x8000)));
|
texture = new Uint16Array(input.slice(offset, (offset+0x8000)));
|
||||||
//texture.forEach(function(indice){console.log(BGR5A1(indice))});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'C': {
|
case 'C': {
|
||||||
// compression format unknown, but all in type use same header format
|
// compression format is RLE-based, where first u32 is size, and format is defined as:
|
||||||
|
/**
|
||||||
|
* u16 rleType;
|
||||||
|
* if (rleType >= 0xff00) {
|
||||||
|
* //do a raw copy
|
||||||
|
* let length = (0xffff - rleType);
|
||||||
|
* byte data[length];
|
||||||
|
* } else {
|
||||||
|
* //repeat next byte rleType times
|
||||||
|
* data = new Uint16Array(rleType);
|
||||||
|
* for (let index = 0; index < rleType; index++) {
|
||||||
|
* data[index] = u16 repeater @ +4;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
**/
|
||||||
|
//output of this will be another u16[0x4000] of the decompressed texture
|
||||||
|
//after that just parse output as-if it was uncompressed, I think.
|
||||||
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))};
|
||||||
}
|
}
|
||||||
|
@ -273,6 +292,14 @@ function readEmsPsuFile(input){
|
||||||
function readPsvFile(input){
|
function readPsvFile(input){
|
||||||
const view = new DataView(input);
|
const view = new DataView(input);
|
||||||
const u32le = function(i){return view.getUint32(i, 1)};
|
const u32le = function(i){return view.getUint32(i, 1)};
|
||||||
|
const t64le = function(i){return {
|
||||||
|
seconds: view.getUint8(i+1),
|
||||||
|
minutes: view.getUint8(i+2),
|
||||||
|
hours: view.getUint8(i+3),
|
||||||
|
day: view.getUint8(i+4),
|
||||||
|
month: view.getUint8(i+5),
|
||||||
|
year: view.getUint16(i+6, 1)
|
||||||
|
}};
|
||||||
//!pattern psv_file.hexpat
|
//!pattern psv_file.hexpat
|
||||||
const magic = u32le(0);
|
const magic = u32le(0);
|
||||||
if (magic !== 0x50535600) {
|
if (magic !== 0x50535600) {
|
||||||
|
@ -280,25 +307,28 @@ function readPsvFile(input){
|
||||||
}
|
}
|
||||||
//:skip 4
|
//:skip 4
|
||||||
//:skip 20 // key seed, console ignores this
|
//:skip 20 // key seed, console ignores this
|
||||||
//:skip 20 // sha1 hmac digest, useful for... something
|
//:skip 20 // sha1 hmac digest, useful for verifying that this is, indeed, save data.
|
||||||
//:skip 8
|
//:skip 8
|
||||||
const type1 = u32le(52);
|
const type1 = u32le(56);
|
||||||
const type2 = u32le(56);
|
const type2 = u32le(60);
|
||||||
if(type1 !== 0x2c && type2 !== 2) {
|
if(type1 !== 0x2c && type2 !== 2) {
|
||||||
throw `Not parsing, this is not a PS2 save export (was ${type1}:${type2}, expected 44:2)`;
|
throw `Not parsing, this is not a PS2 save export (was ${type1}:${type2}, expected 44:2)`;
|
||||||
}
|
}
|
||||||
const displayedSize = u32le(60);
|
const displayedSize = u32le(64);
|
||||||
const ps2dOffset = u32le(64);
|
const ps2dOffset = u32le(68);
|
||||||
const ps2dSize = u32le(68); // don't know why this is included if its always 964
|
const ps2dSize = u32le(72); // don't know why this is included if its always 964
|
||||||
const nModelOffset = u32le(72);
|
const nModelOffset = u32le(76);
|
||||||
const nModelSize = u32le(76);
|
const nModelSize = u32le(80);
|
||||||
const cModelOffset = u32le(80);
|
const cModelOffset = u32le(84);
|
||||||
const cModelSize = u32le(84);
|
const cModelSize = u32le(88);
|
||||||
const dModelOffset = u32le(88);
|
const dModelOffset = u32le(92);
|
||||||
const dModelSize = u32le(92);
|
const dModelSize = u32le(96);
|
||||||
const numberOfFiles = u32le(96); // in-case this library changes stance on other files
|
const numberOfFiles = u32le(100); // in-case this library changes stance on other files
|
||||||
const rootDirectoryData = input.slice(100, 158);
|
// file = {t64le created, t64le modified, u32 size, u32 permissions, byte[32] title}
|
||||||
let offset = 158;
|
// and if it's not the root directory, add another u32 for offset/location
|
||||||
|
const rootDirectoryData = input.slice(104, 162);
|
||||||
|
const timestamps = {created: t64le(104), modified: t64le(112)};
|
||||||
|
let offset = 162;
|
||||||
let fileData = new Array();
|
let fileData = new Array();
|
||||||
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));
|
||||||
|
@ -313,7 +343,7 @@ function readPsvFile(input){
|
||||||
if (ICONJS_DEBUG) {
|
if (ICONJS_DEBUG) {
|
||||||
console.log({magic, type1, type2, displayedSize, ps2dOffset, ps2dSize, nModelOffset, nModelSize, cModelOffset, cModelSize, dModelOffset, dModelSize, numberOfFiles, rootDirectoryData, fileData})
|
console.log({magic, type1, type2, displayedSize, ps2dOffset, ps2dSize, nModelOffset, nModelSize, cModelOffset, cModelSize, dModelOffset, dModelSize, numberOfFiles, rootDirectoryData, fileData})
|
||||||
}
|
}
|
||||||
return {icons, "icon.sys": input.slice(ps2dOffset, ps2dOffset+ps2dSize)};
|
return {icons, "icon.sys": input.slice(ps2dOffset, ps2dOffset+ps2dSize), timestamps};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof module !== "undefined") {
|
if(typeof module !== "undefined") {
|
||||||
|
|
7
index.js
7
index.js
|
@ -16,15 +16,18 @@ if(processObj.argv[2] === "psu") {
|
||||||
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "file.psu");
|
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "file.psu");
|
||||||
const parsed = iconjs.readEmsPsuFile(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
const parsed = iconjs.readEmsPsuFile(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
||||||
const PS2D = iconjs.readPS2D(parsed[parsed.rootDirectory]["icon.sys"].data);
|
const PS2D = iconjs.readPS2D(parsed[parsed.rootDirectory]["icon.sys"].data);
|
||||||
output = {parsed, PS2D}
|
let output = {parsed, PS2D}
|
||||||
Object.keys(PS2D.filenames).forEach(function(file) {
|
Object.keys(PS2D.filenames).forEach(function(file) {
|
||||||
output[file] = iconjs.readIconFile(parsed[parsed.rootDirectory][PS2D.filenames[file]].data);
|
output[file] = iconjs.readIconFile(parsed[parsed.rootDirectory][PS2D.filenames[file]].data);
|
||||||
});
|
});
|
||||||
console.log(output);
|
console.log(output);
|
||||||
} else if(processObj.argv[2] === "psv") {
|
} else if(processObj.argv[2] === "psv") {
|
||||||
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "file.psu");
|
let inputFile = filesystem.readFileSync(processObj.argv[3] ? processObj.argv[3] : "file.psv");
|
||||||
const parsed = iconjs.readPsvFile(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
const parsed = iconjs.readPsvFile(inputFile.buffer.slice(inputFile.byteOffset, inputFile.byteOffset + inputFile.byteLength));
|
||||||
console.log(parsed);
|
console.log(parsed);
|
||||||
|
const PS2D = iconjs.readPS2D(parsed["icon.sys"]);
|
||||||
|
let output = {parsed, PS2D};
|
||||||
|
console.log(output);
|
||||||
} else {
|
} else {
|
||||||
let inputFile = filesystem.readFileSync(processObj.argv[2] ? processObj.argv[2] : "icon.sys");
|
let inputFile = filesystem.readFileSync(processObj.argv[2] ? processObj.argv[2] : "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));
|
||||||
|
|
|
@ -186,6 +186,11 @@
|
||||||
let inputData = readPsvFile(d);
|
let inputData = readPsvFile(d);
|
||||||
let output = readPS2D(inputData["icon.sys"]);
|
let output = readPS2D(inputData["icon.sys"]);
|
||||||
updateDisplay(output);
|
updateDisplay(output);
|
||||||
|
let cTime = inputData.timestamps.created;
|
||||||
|
let mTime = inputData.timestamps.modified;
|
||||||
|
//TODO: use Time() to align JST times to user-local timezone
|
||||||
|
document.getElementById("dateCreated").textContent = `${cTime.hours.toString().padStart("2","0")}:${cTime.minutes.toString().padStart("2","0")}:${cTime.seconds.toString().padStart("2","0")} ${cTime.day.toString().padStart("2","0")}/${cTime.month.toString().padStart("2","0")}/${cTime.year}`;
|
||||||
|
document.getElementById("dateModified").textContent = `${mTime.hours.toString().padStart("2","0")}:${mTime.minutes.toString().padStart("2","0")}:${mTime.seconds.toString().padStart("2","0")} ${mTime.day.toString().padStart("2","0")}/${mTime.month.toString().padStart("2","0")}/${mTime.year}`;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if(glContext!==null){glContext.clear(glContext.COLOR_BUFFER_BIT);}
|
if(glContext!==null){glContext.clear(glContext.COLOR_BUFFER_BIT);}
|
||||||
alert(e);
|
alert(e);
|
||||||
|
|
Loading…
Reference in New Issue