parent
af1359ed5b
commit
4e27fcdc94
70
icon.js
70
icon.js
|
@ -1,7 +1,7 @@
|
|||
//todo: Make this a module/mjs file. C6 compatibility can stay, if needed.
|
||||
ICONJS_DEBUG = false;
|
||||
ICONJS_STRICT = true;
|
||||
ICONJS_VERSION = "0.3.5";
|
||||
ICONJS_VERSION = "0.4.0";
|
||||
|
||||
function setDebug(value) {
|
||||
ICONJS_DEBUG = !!value;
|
||||
|
@ -26,9 +26,60 @@ function getTextureFormat(i) {
|
|||
}
|
||||
}
|
||||
|
||||
function BGR5A1(i) {
|
||||
return {b: (i & 0b11111), g: ((i >> 5) & 0b11111), r: ((i >> 10) & 0b11111), a: (i >> 15)}
|
||||
// map this as *8 for each color and i+1*127 for alpha, GL-wise, float(i+1)
|
||||
function uncompressTexture(texData) {
|
||||
// for texture formats 8-14 (and maybe 15 but that's being weird)
|
||||
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) {
|
||||
|
@ -107,7 +158,8 @@ function readIconFile(input) {
|
|||
r: (i & 0xff),
|
||||
g: ((i & 0xff00) >> 8),
|
||||
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);
|
||||
if (magic !== 0x010000) {
|
||||
|
@ -190,7 +242,7 @@ function readIconFile(input) {
|
|||
* u16 rleType;
|
||||
* if (rleType >= 0xff00) {
|
||||
* //do a raw copy
|
||||
* let length = (0xffff - rleType);
|
||||
* let length = (0x10000 - rleType);
|
||||
* byte data[length];
|
||||
* } else {
|
||||
* //repeat next byte rleType times
|
||||
|
@ -201,7 +253,7 @@ function readIconFile(input) {
|
|||
* }
|
||||
**/
|
||||
//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);
|
||||
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)};
|
||||
//!pattern sps-xps_file.hexpat
|
||||
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;
|
||||
const ident = input.slice(offset, offset+identLength);
|
||||
if((new TextDecoder("utf-8")).decode(ident) !== "SharkPortSave") {
|
||||
|
@ -445,6 +500,7 @@ function readSharkXPortSxpsFile(input) {
|
|||
}
|
||||
}
|
||||
fsOut[header.filename] = output;
|
||||
//:skip 4 // then here lies, at offset (the end of file), a u32 checksum.
|
||||
return fsOut;
|
||||
}
|
||||
|
||||
|
|
91
input.htm
91
input.htm
|
@ -28,26 +28,7 @@
|
|||
attribute vec4 a_color;
|
||||
|
||||
varying lowp vec4 v_color;
|
||||
|
||||
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;
|
||||
varying lowp vec2 v_textureCoords;
|
||||
|
||||
void main() {
|
||||
// x, y, z, scale (w)
|
||||
|
@ -59,6 +40,7 @@
|
|||
);
|
||||
// flip it, scale it
|
||||
v_textureCoords = a_textureCoords;
|
||||
v_color = a_color;
|
||||
}
|
||||
</script>
|
||||
<script type="text/plain" id="shader-icon-f">
|
||||
|
@ -69,7 +51,7 @@
|
|||
}
|
||||
</script>
|
||||
<script type="text/plain" id="shader-icon-f2">
|
||||
varying highp vec2 v_textureCoords;
|
||||
varying lowp vec2 v_textureCoords;
|
||||
uniform sampler2D u_sampler;
|
||||
|
||||
void main() {
|
||||
|
@ -179,68 +161,27 @@
|
|||
document.getElementById("icond").textContent = "?";
|
||||
document.getElementById("dateCreated").textContent = "--:--:-- --/--/----";
|
||||
document.getElementById("dateModified").textContent = "--:--:-- --/--/----";
|
||||
}
|
||||
function uncompressTexture(texData) {
|
||||
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;
|
||||
document.getElementById("fileCommentGame").textContent = "(no title)";
|
||||
document.getElementById("fileCommentName").textContent = "(no description)";
|
||||
}
|
||||
function renderIcon(iconData) {
|
||||
if(glFgContext === null) {return -1;} else {
|
||||
const texture = glFgContext.createTexture();
|
||||
glFgContext.bindTexture(glFgContext.TEXTURE_2D, texture);
|
||||
if (iconData.textureFormat === "U") {
|
||||
glFgContext.texImage2D(glFgContext.TEXTURE_2D, 0, glFgContext.RGBA, 128, 128, 0, glFgContext.RGBA, glFgContext.UNSIGNED_SHORT_5_5_5_1, iconData.texture);
|
||||
glFgContext.generateMipmap(glFgContext.TEXTURE_2D);
|
||||
}
|
||||
if (iconData.textureFormat !== "N") {
|
||||
let rgb5a1_converted;
|
||||
if (iconData.textureFormat === "C") {
|
||||
let uncompressed = uncompressTexture(iconData.texture.data);
|
||||
console.log(uncompressed);
|
||||
/*
|
||||
let uncompressed = new Uint16Array([
|
||||
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);
|
||||
rgb5a1_converted = convertBGR5A1toRGB5A1(uncompressed.buffer);
|
||||
} else {
|
||||
rgb5a1_converted = convertBGR5A1toRGB5A1(iconData.texture.buffer);
|
||||
}
|
||||
glFgContext.texImage2D(glFgContext.TEXTURE_2D, 0, glFgContext.RGBA, 128, 128, 0, glFgContext.RGBA, glFgContext.UNSIGNED_SHORT_5_5_5_1, rgb5a1_converted);
|
||||
glFgContext.generateMipmap(glFgContext.TEXTURE_2D);
|
||||
}
|
||||
//.section PROGRAM.icon
|
||||
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);
|
||||
} else {
|
||||
var iconVertexShader = createShader(glFgContext, glFgContext.VERTEX_SHADER, document.getElementById("shader-icon-v").text);
|
||||
|
@ -385,7 +326,7 @@
|
|||
let inputData = readPsvFile(d);
|
||||
let output = readPS2D(inputData["icon.sys"]);
|
||||
updateDisplay(output);
|
||||
renderIcon(inputData.icons.n);
|
||||
renderIcon(readIconFile(inputData.icons.n));
|
||||
let cTime = inputData.timestamps.created;
|
||||
let mTime = inputData.timestamps.modified;
|
||||
//TODO: use Time() to align JST times to user-local timezone
|
||||
|
@ -506,10 +447,10 @@
|
|||
document.getElementById("showExtractedInputOption").onchange = function(e) {
|
||||
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>
|
||||
<span id="version">icondumper2 <span id="iconjsVersion">(unknown icon.js version)</span> [C: <span id="clientVersion">Loading...</span>] - © 2023 yellows111</span>
|
||||
<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>
|
||||
</html>
|
Loading…
Reference in New Issue