Soft client interpolation update, minor security update.

Interpolation between two frames of animation is now implemented,
and can be controlled with the PERIOD and COMMA keys.

Automatic playing of interpolated frames is not yet complete.

Security:
All objects which can output arbitrary text has their prototype set to null.

Some other objects have also been set to null prototypes,
which are t64le and the yellowDataReader constructor output.

Still yet to parse actual animation data...
Probably need to know how to make requestAnimationFrame loops practical...

Authors Comment: This filled my evening from doing nothing.
This commit is contained in:
yellows111 2024-02-29 13:43:08 +00:00
parent a6ae91dffc
commit f05ad6fac4
2 changed files with 122 additions and 64 deletions

19
icon.js
View File

@ -8,7 +8,7 @@ var ICONJS_STRICT = true;
* @constant {string} * @constant {string}
* @default * @default
*/ */
const ICONJS_VERSION = "0.8.3"; const ICONJS_VERSION = "0.8.4";
/** /**
* The RC4 key used for ciphering CodeBreaker Saves. * The RC4 key used for ciphering CodeBreaker Saves.
@ -92,6 +92,7 @@ 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),
@ -494,8 +495,8 @@ function readEmsPsuFile(input){
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 = {length: header.size, rootDirectory: header.filename, timestamps: header.timestamps}; let fsOut = {"__proto__": null, length: header.size, rootDirectory: header.filename, timestamps: header.timestamps};
let output = new Object(); let output = {"__proto__": null};
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)); const fdesc = readEntryBlock(input.slice(offset, offset + 512));
@ -676,8 +677,8 @@ function readSharkXPortSxpsFile(input) {
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 = {length: header.size, rootDirectory: header.filename, timestamps: header.timestamps, comments}; let fsOut = {"__proto__": null, length: header.size, rootDirectory: header.filename, timestamps: header.timestamps, comments};
let output = new Object(); let output = {"__proto__": null};
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)); const fdesc = readSxpsDescriptor(input.slice(offset, offset + 250));
switch(fdesc.type) { switch(fdesc.type) {
@ -713,7 +714,7 @@ function readSharkXPortSxpsFile(input) {
*/ */
function readCodeBreakerCbsDirectory(input) { function readCodeBreakerCbsDirectory(input) {
const {u32le, t64le} = new yellowDataReader(input); const {u32le, t64le} = new yellowDataReader(input);
const virtualFilesystem = new Object(); const virtualFilesystem = {"__proto__": null};
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);
@ -764,7 +765,7 @@ 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 = {rootDirectory: dirName, timestamps}; const fsOut = {"__proto__": null, rootDirectory: dirName, timestamps};
fsOut[dirName] = readCodeBreakerCbsDirectory(inflatedData); fsOut[dirName] = readCodeBreakerCbsDirectory(inflatedData);
if(ICONJS_DEBUG) { if(ICONJS_DEBUG) {
console.debug({magic, dataOffset, compressedSize, dirName, permissions, displayName}); console.debug({magic, dataOffset, compressedSize, dirName, permissions, displayName});
@ -780,7 +781,7 @@ function readCodeBreakerCbsFile(input, inflator = null) {
*/ */
function readMaxPwsDirectory(input, directorySize) { function readMaxPwsDirectory(input, directorySize) {
const {u32le} = new yellowDataReader(input); const {u32le} = new yellowDataReader(input);
const virtualFilesystem = new Object(); const virtualFilesystem = {"__proto__": null};
let offset = 0; let offset = 0;
for (let index = 0; index < directorySize; index++) { for (let index = 0; index < directorySize; index++) {
const dataSize = u32le(offset); const dataSize = u32le(offset);
@ -828,7 +829,7 @@ function readMaxPwsFile(input, unlzari) {
const size = u32le(0x54); const size = u32le(0x54);
const compressedData = input.slice(88, input.byteLength); const compressedData = input.slice(88, input.byteLength);
const uncompressedData = unlzari(new Uint8Array(compressedData)); // read above why we can't trust given size const uncompressedData = unlzari(new Uint8Array(compressedData)); // read above why we can't trust given size
const fsOut = {rootDirectory: dirName}; const fsOut = {"__proto__": null, rootDirectory: dirName};
fsOut[dirName] = readMaxPwsDirectory(uncompressedData, size); fsOut[dirName] = readMaxPwsDirectory(uncompressedData, size);
// there's no... timestamps or permissions... this doesn't bode well. // there's no... timestamps or permissions... this doesn't bode well.
if(ICONJS_DEBUG) { if(ICONJS_DEBUG) {

167
index.htm
View File

@ -6,8 +6,9 @@
<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... -->
<script src="lzari.js"></script> <script src="lzari.js"></script>
<!-- If you need pako to be optional, remove/comment the bottom line. This will disable support for CBS reading, however --> <!-- 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> <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}
@ -41,12 +42,14 @@
<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;
@ -58,11 +61,12 @@
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(
(a_position.x * pos.x) + (a_position.z * pos.y), //transform the x position (lv_interp.x * pos.x) + (lv_interp.z * pos.y), //transform the x position
(0.0 - a_position.y) - 2.75, // invert the y position and move down -2.75, which will center the model (-lv_interp.y) - 2.75, // invert the y position and move down -2.75, which will center the model
(a_position.x * -pos.y) + (a_position.z * pos.x), //transform the z position (lv_interp.x * -pos.y) + (lv_interp.z * pos.x), //transform the z position
u_scale u_scale
); );
// flip it, scale it // flip it, scale it
@ -133,7 +137,7 @@
<h1 id="title1">&#xFF2E;&#xFF4F;&#x3000;&#xFF26;&#xFF49;&#xFF4C;&#xFF45;</h1> <h1 id="title1">&#xFF2E;&#xFF4F;&#x3000;&#xFF26;&#xFF49;&#xFF4C;&#xFF45;</h1>
<h1 id="title2">&#xFF2C;&#xFF4F;&#xFF41;&#xFF44;&#xFF45;&#xFF44;</h1> <h1 id="title2">&#xFF2C;&#xFF4F;&#xFF41;&#xFF44;&#xFF45;&#xFF44;</h1>
</div> </div>
<span>Background/icon preview (Keyboard controls: rotate: &larr;/&rarr;, scale: &uarr;/&darr;, frame-step: &minus;/&equals;, change icon: 1:N/2:C/3:D):</span><br> <span>Background/icon preview (Keyboard controls: rotate: &larr;/&rarr;, scale: &uarr;/&darr;, step: &minus;/=, play: &lt;/&gt;, 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>
@ -179,8 +183,12 @@
<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 await onload() again // I usually don't do in-body <script>'s, but I didn't want to do an awaited onload() again
const GlobalState = {rotations: 2, dataLength: 0, uniforms: {rotation: null, scale: null}, iconState: {source: null, currentIcon: null, currentSubmodel: 0, cachedIconSys: null}, fileReader: (new FileReader)}; const GlobalState = {"__proto__": null, rotations: 2, scale: 0, interpfactor: 0, dataLength: 0,
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(
@ -191,10 +199,15 @@
} }
); );
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
const rotationDensity = 60; var 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") {
@ -224,6 +237,7 @@
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;
} }
@ -232,6 +246,7 @@
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;
} }
@ -253,12 +268,41 @@
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;}
} }
@ -292,7 +336,7 @@
} }
} }
function resetDisplay() { function resetDisplay() {
//reset displayed elements // reset displayed elements
document.getElementById("title1").textContent = "\uff0d"; document.getElementById("title1").textContent = "\uff0d";
document.getElementById("title2").textContent = "\uff0d"; document.getElementById("title2").textContent = "\uff0d";
document.getElementById("iconn").textContent = "?"; document.getElementById("iconn").textContent = "?";
@ -303,7 +347,7 @@
document.getElementById("fileCommentGame").textContent = "(no title)"; document.getElementById("fileCommentGame").textContent = "(no title)";
document.getElementById("fileCommentName").textContent = "(no description)"; document.getElementById("fileCommentName").textContent = "(no description)";
document.getElementById("fileCommentDesc").textContent = "(no other text)"; document.getElementById("fileCommentDesc").textContent = "(no other text)";
//reset globalstate parameters // reset globalstate parameters
GlobalState.iconState.cachedIconSys = null; GlobalState.iconState.cachedIconSys = null;
GlobalState.iconState.currentIcon = null; GlobalState.iconState.currentIcon = null;
GlobalState.iconState.source = null; GlobalState.iconState.source = null;
@ -312,7 +356,7 @@
GlobalState.dataLength = 0; GlobalState.dataLength = 0;
GlobalState.rotations = 2; GlobalState.rotations = 2;
GlobalState.iconState.currentSubmodel = 0; GlobalState.iconState.currentSubmodel = 0;
//clear buffers // clear buffers
if(glFgContext !== null) { if(glFgContext !== null) {
glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT); glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);
} }
@ -323,10 +367,10 @@
"lighting": { "lighting": {
"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}, //p[0] {r:1,g:0,b:0,a:1}, // point0
{r:0,g:1,b:0,a:1}, //p[1] {r:0,g:1,b:0,a:1}, // point1
{r:0,g:0,b:1,a:1} //p[2] {r:0,g:0,b:1,a:1} // point2
] ]
} }
}; };
@ -355,37 +399,37 @@
} }
let iconProgram = createProgram(glFgContext, iconVertexShader, iconFragmentShader); let iconProgram = createProgram(glFgContext, iconVertexShader, iconFragmentShader);
glFgContext.useProgram(iconProgram); glFgContext.useProgram(iconProgram);
var attributes = {
color: glFgContext.getAttribLocation(iconProgram, "a_color"),
position: glFgContext.getAttribLocation(iconProgram, "a_position"),
nextPosition: glFgContext.getAttribLocation(iconProgram, "a_nextPosition")
};
var uniforms = {
ambientLighting: glFgContext.getUniformLocation(iconProgram, "u_ambientLight"),
scale: glFgContext.getUniformLocation(iconProgram, "u_scale"),
interpolation: glFgContext.getUniformLocation(iconProgram, "u_interp"),
rotation: glFgContext.getUniformLocation(iconProgram, "u_rotation")
}
if(iconData.textureFormat !== "N") { if(iconData.textureFormat !== "N") {
var attributes = { attributes["textureCoords"] = glFgContext.getAttribLocation(iconProgram, "a_textureCoords");
position: glFgContext.getAttribLocation(iconProgram, "a_position"), uniforms["sampler"] = glFgContext.getUniformLocation(iconProgram, "u_sampler");
textureCoords: glFgContext.getAttribLocation(iconProgram, "a_textureCoords"),
color: glFgContext.getAttribLocation(iconProgram, "a_color"),
};
var uniforms = {
rotation: glFgContext.getUniformLocation(iconProgram, "u_rotation"),
ambientLighting: glFgContext.getUniformLocation(iconProgram, "u_ambientLight"),
scale: glFgContext.getUniformLocation(iconProgram, "u_scale")
}
} 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")
}
} }
//.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);
@ -393,12 +437,19 @@
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);
@ -421,21 +472,27 @@
// sets the angle uniform to 2/rotationDensity, this puts the icon at an angle. // sets the angle uniform to 2/rotationDensity, this puts the icon at an angle.
// globalize uniform rotation // globalize uniform rotation
GlobalState.uniforms.rotation = uniforms.rotation; GlobalState.uniforms.rotation = uniforms.rotation;
if(clearData){GlobalState.rotations = 2;} if (clearData) {GlobalState.rotations = 2;}
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]; let colours = fileMetadata.lighting.colors[0]; // get ambient lighting colours
//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
GlobalState.uniforms.scale = uniforms.scale; GlobalState.uniforms.scale = uniforms.scale;
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);
glFgContext.drawArrays(glFgContext.TRIANGLES, 0, GlobalState.dataLength); glFgContext.drawArrays(glFgContext.TRIANGLES, 0, GlobalState.dataLength);
} }
@ -507,9 +564,9 @@
renderIcon(output2.n, output); renderIcon(output2.n, output);
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 = `${p0in(cTime.hours.toString())}:${p0in(cTime.minutes.toString())}:${p0in(cTime.seconds.toString())} ${p0in(cTime.day.toString())}/${p0in(cTime.month.toString())}/${cTime.year}`; document.getElementById("dateCreated").textContent = timeToString(cTime);
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("dateModified").textContent = timeToString(mTime);
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) {
@ -545,9 +602,9 @@
renderIcon(icons.n, output); renderIcon(icons.n, output);
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 = `${p0in(cTime.hours.toString())}:${p0in(cTime.minutes.toString())}:${p0in(cTime.seconds.toString())} ${p0in(cTime.day.toString())}/${p0in(cTime.month.toString())}/${cTime.year}`; document.getElementById("dateCreated").textContent = timeToString(cTime);
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("dateModified").textContent = timeToString(mTime);
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) {
@ -582,9 +639,9 @@
renderIcon(output2.n, output); renderIcon(output2.n, output);
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 = `${p0in(cTime.hours.toString())}:${p0in(cTime.minutes.toString())}:${p0in(cTime.seconds.toString())} ${p0in(cTime.day.toString())}/${p0in(cTime.month.toString())}/${cTime.year}`; document.getElementById("dateCreated").textContent = timeToString(cTime);
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("dateModified").textContent = timeToString(mTime);
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")) {
@ -627,14 +684,14 @@
renderIcon(output2.n, output); renderIcon(output2.n, output);
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
if(cTime.year === 0) { if(cTime.year === 0) {
// if root directory time is null, read icon.sys instead // if root directory time is null, read icon.sys instead
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 = `${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("dateCreated").textContent = timeToString(cTime);
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()}`; document.getElementById("dateModified").textContent = timeToString(mTime);
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) {
@ -759,12 +816,12 @@
if (typeof decodeLzari === "undefined") { if (typeof decodeLzari === "undefined") {
document.getElementById("maxinput").disabled = true; document.getElementById("maxinput").disabled = true;
} }
//todo: More than one model shape rendering, other 2 icons (technically done? NMW though), Animation parsing, animation tweening // 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>] &mdash; &copy; <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>] &mdash; &copy; <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.0+u2"; document.getElementById("clientVersion").textContent = "0.8.1";
document.getElementById("currentYear").textContent = (new Date()).getFullYear().toString(); document.getElementById("currentYear").textContent = (new Date()).getFullYear().toString();
</script> </script>
</body> </body>