Oh my! Working textures!

Looking good, eh?
This commit is contained in:
yellows111 2023-10-17 20:59:54 +01:00
parent af1359ed5b
commit 4e27fcdc94
2 changed files with 81 additions and 84 deletions

70
icon.js
View File

@ -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.5"; ICONJS_VERSION = "0.4.0";
function setDebug(value) { function setDebug(value) {
ICONJS_DEBUG = !!value; ICONJS_DEBUG = !!value;
@ -26,9 +26,60 @@ function getTextureFormat(i) {
} }
} }
function BGR5A1(i) { function uncompressTexture(texData) {
return {b: (i & 0b11111), g: ((i >> 5) & 0b11111), r: ((i >> 10) & 0b11111), a: (i >> 15)} // for texture formats 8-14 (and maybe 15 but that's being weird)
// map this as *8 for each color and i+1*127 for alpha, GL-wise, float(i+1) if (texData.length & 1) {
throw "Texture size isn't a multiple of 2 (was ${texData.length})";
}
const view = new DataView(texData);
const u16le = function(i){return view.getUint16(i, 1)}
let uncompressed = new Uint16Array(16384);
let offset = 0;
for (let index = 0; index < 16384; index++) {
currentValue = u16le(offset);
offset += 2;
if (currentValue >= 0xff00) {
//do a raw copy
let length = ((0x10000 - currentValue));
for (let enumerator = 0; enumerator < length; enumerator++) {
uncompressed[index] = u16le(offset);
offset += 2;
index++;
}
} else {
//repeat next byte rleType times
uncompressed[index] = u16le(offset);
for (let indey = 0; indey < currentValue; indey++) {
uncompressed[index] = u16le(offset);
index++
}
offset += 2;
}
index--;
}
return uncompressed;
}
function convertBGR5A1toRGB5A1(bgrData) {
if(bgrData.byteLength !== 32768) {
throw `Not a 128x128x16 texture. (length was ${bgrData.length})`;
}
// converts 5-bit blue, green, red (in that order) with one alpha bit to GL-compatible RGB5A1
const view = new DataView(bgrData);
const u16le = function(i){return view.getUint16(i, 1)}
let converted = new Uint16Array(16384);
for (let index = 0; index < 16384; index++) {
let b = ( u16le(index*2) & 0b11111);
let g = (((u16le(index*2)) >> 5) & 0b11111);
let r = (((u16le(index*2)) >> 10) & 0b11111);
// rrrrrgggggbbbbba (a = 1 because 0 is 127 which is 1.0f opacity for the GS)
let newValue = 0b0000000000000001;
newValue |= (r << 1);
newValue |= (g << 6);
newValue |= (b << 11);
converted[index] = newValue;
}
return converted;
} }
function stringScrubber(dirty) { function stringScrubber(dirty) {
@ -107,7 +158,8 @@ function readIconFile(input) {
r: (i & 0xff), r: (i & 0xff),
g: ((i & 0xff00) >> 8), g: ((i & 0xff00) >> 8),
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)
a: 255 // I don't think alpha transparency is actually USED in icons?
}}; }};
const magic = u32le(0); const magic = u32le(0);
if (magic !== 0x010000) { if (magic !== 0x010000) {
@ -190,7 +242,7 @@ function readIconFile(input) {
* u16 rleType; * u16 rleType;
* if (rleType >= 0xff00) { * if (rleType >= 0xff00) {
* //do a raw copy * //do a raw copy
* let length = (0xffff - rleType); * let length = (0x10000 - rleType);
* byte data[length]; * byte data[length];
* } else { * } else {
* //repeat next byte rleType times * //repeat next byte rleType times
@ -201,7 +253,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, I think. //after that just parse output as-if it was uncompressed.
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))};
} }
@ -399,6 +451,9 @@ function readSharkXPortSxpsFile(input) {
const u32le = function(i){return view.getUint32(i, 1)}; const u32le = function(i){return view.getUint32(i, 1)};
//!pattern sps-xps_file.hexpat //!pattern sps-xps_file.hexpat
const identLength = u32le(0); const identLength = u32le(0);
if(identLength !== 13) {
throw `Not a SharkPort (SPS) or X-Port (XPS) export file (was ${identLength}, expected 13)`;
}
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") {
@ -445,6 +500,7 @@ function readSharkXPortSxpsFile(input) {
} }
} }
fsOut[header.filename] = output; fsOut[header.filename] = output;
//:skip 4 // then here lies, at offset (the end of file), a u32 checksum.
return fsOut; return fsOut;
} }

View File

@ -28,26 +28,7 @@
attribute vec4 a_color; attribute vec4 a_color;
varying lowp vec4 v_color; varying lowp vec4 v_color;
varying lowp vec2 v_textureCoords;
void main() {
// x, y, z, scale (w)
gl_Position = vec4(
a_position.x,
(0.0 - a_position.y) - 2.75, // invert the y position and move down -2.75, which will center the model
a_position.z,
3.0
);
// flip it, scale it
v_color = a_color;
}
</script>
<script type="text/plain" id="shader-icon-v2">
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_textureCoords;
attribute vec4 a_color;
varying highp vec2 v_textureCoords;
void main() { void main() {
// x, y, z, scale (w) // x, y, z, scale (w)
@ -59,6 +40,7 @@
); );
// flip it, scale it // flip it, scale it
v_textureCoords = a_textureCoords; v_textureCoords = a_textureCoords;
v_color = a_color;
} }
</script> </script>
<script type="text/plain" id="shader-icon-f"> <script type="text/plain" id="shader-icon-f">
@ -69,7 +51,7 @@
} }
</script> </script>
<script type="text/plain" id="shader-icon-f2"> <script type="text/plain" id="shader-icon-f2">
varying highp vec2 v_textureCoords; varying lowp vec2 v_textureCoords;
uniform sampler2D u_sampler; uniform sampler2D u_sampler;
void main() { void main() {
@ -179,68 +161,27 @@
document.getElementById("icond").textContent = "?"; document.getElementById("icond").textContent = "?";
document.getElementById("dateCreated").textContent = "--:--:-- --/--/----"; document.getElementById("dateCreated").textContent = "--:--:-- --/--/----";
document.getElementById("dateModified").textContent = "--:--:-- --/--/----"; document.getElementById("dateModified").textContent = "--:--:-- --/--/----";
} document.getElementById("fileCommentGame").textContent = "(no title)";
function uncompressTexture(texData) { document.getElementById("fileCommentName").textContent = "(no description)";
const view = new DataView(texData);
const u16le = function(i){return view.getUint16(i, 1)}
let uncompressed = new Uint16Array(16384);
for (let index = 0; index < 16384; index++) {
currentValue = u16le(index);
console.log(index, currentValue);
if (currentValue >= 0xff00) {
//do a raw copy
let length = ((0x10000 - currentValue));
for (let enumerator = 0; enumerator < length; enumerator++) {
console.log("i'm here", index, (index*2), u16le(index), u16le(index *2), ((index * 2) -2), u16le((index * 2) -2))
uncompressed[index-1] = u16le((index * 2) -2);
index++;
}
} else {
//repeat next byte rleType times
let original = index;
for (let indey = 0; indey < currentValue; indey++) {
uncompressed[index++] = u16le(original+2);
}
}
}
return uncompressed;
} }
function renderIcon(iconData) { function renderIcon(iconData) {
if(glFgContext === null) {return -1;} else { if(glFgContext === null) {return -1;} else {
const texture = glFgContext.createTexture(); const texture = glFgContext.createTexture();
glFgContext.bindTexture(glFgContext.TEXTURE_2D, texture); glFgContext.bindTexture(glFgContext.TEXTURE_2D, texture);
if (iconData.textureFormat === "U") { if (iconData.textureFormat !== "N") {
glFgContext.texImage2D(glFgContext.TEXTURE_2D, 0, glFgContext.RGBA, 128, 128, 0, glFgContext.RGBA, glFgContext.UNSIGNED_SHORT_5_5_5_1, iconData.texture); let rgb5a1_converted;
glFgContext.generateMipmap(glFgContext.TEXTURE_2D); if (iconData.textureFormat === "C") {
} let uncompressed = uncompressTexture(iconData.texture.data);
if (iconData.textureFormat === "C") { rgb5a1_converted = convertBGR5A1toRGB5A1(uncompressed.buffer);
let uncompressed = uncompressTexture(iconData.texture.data); } else {
console.log(uncompressed); rgb5a1_converted = convertBGR5A1toRGB5A1(iconData.texture.buffer);
/* }
let uncompressed = new Uint16Array([ glFgContext.texImage2D(glFgContext.TEXTURE_2D, 0, glFgContext.RGBA, 128, 128, 0, glFgContext.RGBA, glFgContext.UNSIGNED_SHORT_5_5_5_1, rgb5a1_converted);
63551, 63551, 1, 1, 63551, 63551, 1, 1,
63551, 63551, 1, 1, 63551, 63551, 1, 1,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
63551, 63551, 1, 1, 63551, 63551, 1, 1,
63551, 63551, 1, 1, 63551, 63551, 1, 1,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
63551, 63551, 1, 1, 63551, 63551, 1, 1,
63551, 63551, 1, 1, 63551, 63551, 1, 1,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
63551, 63551, 1, 1, 63551, 63551, 1, 1,
63551, 63551, 1, 1, 63551, 63551, 1, 1,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
1, 1, 63551, 63551, 1, 1, 63551, 63551,
]);*/
glFgContext.texImage2D(glFgContext.TEXTURE_2D, 0, glFgContext.RGBA, 128, 128, 0, glFgContext.RGBA, glFgContext.UNSIGNED_SHORT_5_5_5_1, uncompressed);
glFgContext.generateMipmap(glFgContext.TEXTURE_2D); glFgContext.generateMipmap(glFgContext.TEXTURE_2D);
} }
//.section PROGRAM.icon //.section PROGRAM.icon
if(iconData.textureFormat !== "N") { if(iconData.textureFormat !== "N") {
var iconVertexShader = createShader(glFgContext, glFgContext.VERTEX_SHADER, document.getElementById("shader-icon-v2").text); var iconVertexShader = createShader(glFgContext, glFgContext.VERTEX_SHADER, document.getElementById("shader-icon-v").text);
var iconFragmentShader = createShader(glFgContext, glFgContext.FRAGMENT_SHADER, document.getElementById("shader-icon-f2").text); var iconFragmentShader = createShader(glFgContext, glFgContext.FRAGMENT_SHADER, document.getElementById("shader-icon-f2").text);
} else { } else {
var iconVertexShader = createShader(glFgContext, glFgContext.VERTEX_SHADER, document.getElementById("shader-icon-v").text); var iconVertexShader = createShader(glFgContext, glFgContext.VERTEX_SHADER, document.getElementById("shader-icon-v").text);
@ -385,7 +326,7 @@
let inputData = readPsvFile(d); let inputData = readPsvFile(d);
let output = readPS2D(inputData["icon.sys"]); let output = readPS2D(inputData["icon.sys"]);
updateDisplay(output); updateDisplay(output);
renderIcon(inputData.icons.n); renderIcon(readIconFile(inputData.icons.n));
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
@ -506,10 +447,10 @@
document.getElementById("showExtractedInputOption").onchange = function(e) { document.getElementById("showExtractedInputOption").onchange = function(e) {
document.getElementById("advanced").style.display = ((e.target.checked) ? "block" : "none"); document.getElementById("advanced").style.display = ((e.target.checked) ? "block" : "none");
} }
//todo: Texture parsing, Animation parsing, animation tweening //todo: Animation parsing, animation tweening, whatever's going on with texture type 15
</script> </script>
<span id="version">icondumper2 <span id="iconjsVersion">(unknown icon.js version)</span> [C: <span id="clientVersion">Loading...</span>] - &copy; 2023 yellows111</span> <span id="version">icondumper2 <span id="iconjsVersion">(unknown icon.js version)</span> [C: <span id="clientVersion">Loading...</span>] - &copy; 2023 yellows111</span>
<script>document.getElementById("iconjsVersion").textContent = ICONJS_VERSION;</script> <script>document.getElementById("iconjsVersion").textContent = ICONJS_VERSION;</script>
<script>document.getElementById("clientVersion").textContent = "0.4.3";</script> <script>document.getElementById("clientVersion").textContent = "0.5.1";</script>
</body> </body>
</html> </html>