A compatibility update.

No, this isn't a late 'Fool's joke, I was just having fun and now I can target pure ES6/ES2015 if I wanted to.

…except Chakra ('Edge <= 44), that thing never implemented `TextDecoder`/`TextEncoder` for some reason. We'll never know why...

I'm researching what makes over-bright happen... give me time!
This commit is contained in:
yellows111 2023-12-05 18:40:46 +00:00
parent 5738fbb09c
commit 98edefff2e
4 changed files with 124 additions and 58 deletions

View File

@ -1,7 +1,20 @@
# icondumper2 (working title) # icondumper2 (working title)
A JavaScript library (sorta) to read PS2 icons, and their related formats. A JavaScript library (sorta) to read PS2 icons, and their related formats.
## What it supports ## What is a "PS2 icon"?
A set of vertices with may or may not include a texture while defining colours for those vertices.
## Why?
Current implementations had some issues with rendering some icons. These were mostly:
* Not rendering any color for texture type 3.
* Failing to decompress some specific RLE-compressed icons. (types above 8)
* Requires writing/reading a specific format for successful output of data.
As of writing, there was no exporter that exists for the format that exhibited one of these problems.
**icondumper2** is the result of me trying to avoid these problems.
## What it supports:
* EMS Memory Adapter export files (.psu) * EMS Memory Adapter export files (.psu)
* PS3 virtual memory card export files (.psv) * PS3 virtual memory card export files (.psv)
* SharkPort export files (.sps) * SharkPort export files (.sps)
@ -9,30 +22,41 @@ A JavaScript library (sorta) to read PS2 icons, and their related formats.
* PS2 icons (.ico, .icn) * PS2 icons (.ico, .icn)
* PS2D format (icon.sys) * PS2D format (icon.sys)
## What can it do ## What can it do:
* Allow any file in a PSU's or SPS/XPS's virtual filesystem to be dumped. * Allow any file in a supported virtual filesystem (such as those from export files) to be read.
* Warn of invalid icon.sys display names. * Warn of invalid icon.sys display names.
* Read and parse an EMS MA export file. * Export the icon model, with all seperate shapes included as a JavaScript Object.
* Export the icon model, with all seperate shapes included to a JavaScript Object. * CommonJS (that includes node!) module exporting while still being compatible with other JavaScript implementations.
* Node.js compatible (CommonJS) 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 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.
## What it doesn't do ## What it doesn't do:
* (Re)build save files. * Create, manipulate or otherwise taint save files.
* Modify the original input files. * Modify any original input files.
* Use any Node.js-exclusive features. * Use any implementation-specific features.
## Client compatibility ## Client compatibility:
(todo: write this) The library requires use of `const`, `let` and `class` declarations.
Any JavaScript implementation should work if they support all three of these declarations.
### Tested clients:
* Chrome (or Blink-based browser) 49 (or higher) - HTML reference client
* Firefox (or Gecko-based browser) 45 (or higher) - HTML reference client
* Node.js 13 (or higher) - Example client and glTF 2 exporter.
## Why "icondumper2"? ## Why "icondumper2"?
Because it replaced what *was* left of icondumper (1). 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. |
## Included example files:
| Directory | Description | Formats |
| ------------ | ----------------------------------------- | -------- |
| /example_001 | A tetrahedron with all 3 base colors set. | PSU, SPS |

View File

@ -249,7 +249,7 @@ function loadAndConvertIcon(inputData, attemptedFilename = "-") {
if (inputData.hasOwnProperty("numberOfShapes") === false) { if (inputData.hasOwnProperty("numberOfShapes") === false) {
throw "Expected a icondumper2 Intermediate Model Format object."; throw "Expected a icondumper2 Intermediate Model Format object.";
} }
const filename = encodeURIComponent(attemptedFilename).replaceAll(/\%[0-9A-F]{2,2}/g, "").replaceAll(".", "_"); const filename = encodeURIComponent(attemptedFilename).replace(/\%[0-9A-F]{2,2}/g, "").replace(/\./g, "_");
const glTF_output = imf2gltf(inputData, filename); const glTF_output = imf2gltf(inputData, filename);
for (let index = 0; index < (inputData.numberOfShapes); index++) { for (let index = 0; index < (inputData.numberOfShapes); index++) {
(require("fs")).writeFileSync(`${filename}_${index}.gltf`, new TextEncoder().encode(JSON.stringify(glTF_output.objects[index]))); (require("fs")).writeFileSync(`${filename}_${index}.gltf`, new TextEncoder().encode(JSON.stringify(glTF_output.objects[index])));

30
icon.js
View File

@ -1,21 +1,22 @@
//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.
//LOOKING FOR: LZARI implementation (for MAX), description of CBS compression (node zlib doesn't tackle it, even with RC4'ing the data) //LOOKING FOR: LZARI implementation (for MAX), description of CBS compression (node zlib doesn't tackle it, even with RC4'ing the data)
ICONJS_DEBUG = false; var ICONJS_DEBUG = false;
ICONJS_STRICT = true; var ICONJS_STRICT = true;
/** /**
* The current version of the library. * The current version of the library.
* @constant {string} * @constant {string}
* @default * @default
*/ */
const ICONJS_VERSION = "0.6.1"; const ICONJS_VERSION = "0.6.1+u1";
/** /**
* Extension of DataView to add shortcuts for datatypes that I use often. * Extension of DataView to add shortcuts for datatypes that I use often.
* @augments DataView * @augments DataView
* @constructor * @constructor
* @param {ArrayBuffer} buffer ArrayBuffer to base DataView from. * @param {ArrayBuffer} buffer ArrayBuffer to base DataView from.
* @returns {Object.<string, function(number): number>} * @returns {Object.<string, function(number): number>} [u16le, f16le, u32le, f32le]
* @returns {Object.<string, function(number): Object.<string.number>>} [t64le]
* @access protected * @access protected
*/ */
class yellowDataReader extends DataView { class yellowDataReader extends DataView {
@ -40,9 +41,16 @@ class yellowDataReader extends DataView {
*/ */
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 going to be offseted for JST (GMT+09:00). * Time returned is going to be offseted for JST (GMT+09:00).
* @param {number} i Indice offset. * @param {number} i Indice offset.
* @returns {Object.<string, number>} * @returns {Object.<string, number>}
* @property {number} seconds - Seconds.
* @property {number} minutes - Minutes.
* @property {number} hours - Hours.
* @property {number} day - Day.
* @property {number} month - Month.
* @property {number} year - Year.
*/ */
t64le(i){return { t64le(i){return {
seconds: super.getUint8(i+1), seconds: super.getUint8(i+1),
@ -78,7 +86,7 @@ function setDebug(value) {
* Select if invalid characters in titles should be replaced with either spaces or nulls * Select if invalid characters in titles should be replaced with either spaces or nulls
* @param {boolean} value - true: with nulls, false: with spaces * @param {boolean} value - true: with nulls, false: with spaces
* @default true * @default true
* @deprecated unlikely to ever need this? * @deprecated Hasn't been needed for a while. Dropping support by ESM transition.
* @public * @public
*/ */
function setStrictness(value) { function setStrictness(value) {
@ -182,7 +190,7 @@ function convertBGR5A1toRGB5A1(bgrData) {
* @access protected * @access protected
*/ */
function stringScrubber(dirty) { function stringScrubber(dirty) {
return dirty.replaceAll("\x00","").substring(0, (dirty.indexOf("\x00") === -1) ? dirty.length : dirty.indexOf("\x00")); return dirty.replace(/\0/g, "").substring(0, (dirty.indexOf("\x00") === -1) ? dirty.length : dirty.indexOf("\x00"));
} }
/** /**
@ -627,9 +635,9 @@ function readSharkXPortSxpsFile(input) {
} }
/** /**
* Define (module.)exports with all public functions * Define (module.)exports with all public functions.
* @exports icondumper2/icon * @exports icondumper2/icon
*/ */ // start c6js
exports = { exports = {
readers: {readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile}, readers: {readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile},
helpers: {uncompressTexture, convertBGR5A1toRGB5A1}, helpers: {uncompressTexture, convertBGR5A1toRGB5A1},
@ -640,3 +648,9 @@ exports = {
if(typeof module !== "undefined") { if(typeof module !== "undefined") {
module.exports = exports; module.exports = exports;
} }
//end c6js
//start esm
/*export {
readIconFile, readPS2D, readEmsPsuFile, readPsvFile, readSharkXPortSxpsFile, uncompressTexture, convertBGR5A1toRGB5A1, setDebug, ICONJS_VERSION
};*/
//end esm

View File

@ -20,6 +20,13 @@
input[type="file"] {line-height: 2em;} input[type="file"] {line-height: 2em;}
#version {text-shadow: 1px 1px 2px black;} #version {text-shadow: 1px 1px 2px black;}
a {color: #ccc;} a {color: #ccc;}
.inputbox {
display: inline-grid;
margin-right: 0.25em;
border: 1px gray solid;
padding: 0.175em 0.25em 0 0.25em;
margin-bottom: 4px;
}
</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">
@ -133,15 +140,18 @@
<br> <br>
</div> </div>
<hr> <hr>
<div class="inputbox">
<label for="psuinput">EMS Memory Adapter export file (.psu) goes here:</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" />
<br> </div>
<div class="inputbox">
<label for="psvinput">PS3 export file (.psv) goes here:</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" />
<br> </div>
<div class="inputbox">
<label for="spsinput">SharkPort/X-Port export file (.sps, .xps) goes here:</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" />
<br> </div>
<p> <p>
<span>Date&nbsp;created: </span><span id="dateCreated">--:--:--&nbsp;--/--/----</span><span> UTC+09:00</span> <span>Date&nbsp;created: </span><span id="dateCreated">--:--:--&nbsp;--/--/----</span><span> UTC+09:00</span>
<wbr><span>&ndash;</span> <wbr><span>&ndash;</span>
@ -152,16 +162,19 @@
</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 await onload() again
const GlobalState = {rotations: 2, dataLength: 0, uniforms: {rotation: null, scale: null}, iconState: {source: null, currentIcon: null, currentSubmodel: 0, cachedIconSys: null}}; const GlobalState = {rotations: 2, dataLength: 0, uniforms: {rotation: null, scale: null}, iconState: {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"]');
allInputs.forEach( Array.from(allInputs).forEach(
function(nodeObject) { function(nodeObject) {
nodeObject.onclick = function() { nodeObject.onclick = function() {
allInputs.forEach(function(elementObject) {elementObject.value = null;}); Array.from(allInputs).forEach(function(elementObject) {elementObject.value = null;});
} }
} }
); );
function p0in(input) { // "prefix 0 if needed"
return ((input.length>=2) ? input : `0${input}`);
};
// rotation stuff // rotation stuff
const rotationDensity = 60; const rotationDensity = 60;
document.body.onkeydown = function(ev) { document.body.onkeydown = function(ev) {
@ -394,7 +407,7 @@
//.section LIGHTING //.section LIGHTING
let colours = fileMetadata.lighting.colors[0]; 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);
glFgContext.uniform3f(uniforms.ambientLighting, 1, 1, 1); glFgContext.uniform3f(uniforms.ambientLighting, 0.75, 0.75, 0.75);
//.section SCALING //.section SCALING
GlobalState.uniforms.scale = uniforms.scale; GlobalState.uniforms.scale = uniforms.scale;
@ -421,25 +434,30 @@
if(filebox.files.length === 0) { if(filebox.files.length === 0) {
return; return;
} }
filebox.files[0].arrayBuffer().then(function(d){ GlobalState.fileReader.readAsArrayBuffer(filebox.files[0]);
GlobalState.fileReader.onloadend = function() {
GlobalState.fileReader.onloadend = void(0);
try { try {
let output = readPS2D(d); let output = readPS2D(GlobalState.fileReader.result);
console.info("icon.sys", output); console.info("icon.sys", output);
updateDisplay(output); updateDisplay(output);
} catch(e) { } catch(e) {
if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);} if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);}
console.error(e);
alert(e); alert(e);
} }
}); }
} }
iconbox = document.getElementById("icon"); iconbox = document.getElementById("icon");
iconbox.onchange = function(e) { iconbox.onchange = function(e) {
if(iconbox.files.length === 0) { if(iconbox.files.length === 0) {
return; return;
} }
iconbox.files[0].arrayBuffer().then(function(d){ GlobalState.fileReader.readAsArrayBuffer(iconbox.files[0]);
GlobalState.fileReader.onloadend = function() {
GlobalState.fileReader.onloadend = void(0);
try { try {
let output = readIconFile(d); let output = readIconFile(GlobalState.fileReader.result);
GlobalState.iconState.cachedIconSys = null; GlobalState.iconState.cachedIconSys = null;
GlobalState.iconState.source = null; GlobalState.iconState.source = null;
GlobalState.iconState.currentSubmodel = 0; GlobalState.iconState.currentSubmodel = 0;
@ -448,9 +466,10 @@
console.info("model data (ic*)",output); console.info("model data (ic*)",output);
} catch(e) { } catch(e) {
if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);} if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);}
console.error(e);
alert(e); alert(e);
} }
}); }
} }
psubox = document.getElementById("psuinput"); psubox = document.getElementById("psuinput");
psubox.onchange = function(e) { psubox.onchange = function(e) {
@ -458,9 +477,11 @@
if(psubox.files.length === 0) { if(psubox.files.length === 0) {
return; return;
} }
psubox.files[0].arrayBuffer().then(function(d){ GlobalState.fileReader.readAsArrayBuffer(psubox.files[0]);
GlobalState.fileReader.onloadend = function() {
GlobalState.fileReader.onloadend = void(0);
try { try {
let vFilesystem = readEmsPsuFile(d); let vFilesystem = readEmsPsuFile(GlobalState.fileReader.result);
let output = readPS2D(vFilesystem[vFilesystem.rootDirectory]["icon.sys"].data); let output = readPS2D(vFilesystem[vFilesystem.rootDirectory]["icon.sys"].data);
updateDisplay(output); updateDisplay(output);
let output2 = new Object(); let output2 = new Object();
@ -475,16 +496,17 @@
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 = `${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("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 = `${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}`; 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) {
if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);} if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);}
if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);} if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);}
console.error(e);
alert(e); alert(e);
} }
}); }
} }
psvbox = document.getElementById("psvinput"); psvbox = document.getElementById("psvinput");
psvbox.onchange = function(e) { psvbox.onchange = function(e) {
@ -492,9 +514,11 @@
if(psvbox.files.length === 0) { if(psvbox.files.length === 0) {
return; return;
} }
psvbox.files[0].arrayBuffer().then(function(d){ GlobalState.fileReader.readAsArrayBuffer(psvbox.files[0]);
GlobalState.fileReader.onloadend = function() {
GlobalState.fileReader.onloadend = void(0);
try { try {
let inputData = readPsvFile(d); let inputData = readPsvFile(GlobalState.fileReader.result);
let output = readPS2D(inputData["icon.sys"]); let output = readPS2D(inputData["icon.sys"]);
updateDisplay(output); updateDisplay(output);
const icons = { const icons = {
@ -510,16 +534,17 @@
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 = `${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("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 = `${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}`; 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) {
if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);} if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);}
if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);} if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);}
console.error(e);
alert(e); alert(e);
} }
}); }
} }
spsbox = document.getElementById("spsinput"); spsbox = document.getElementById("spsinput");
spsbox.onchange = function(e) { spsbox.onchange = function(e) {
@ -527,9 +552,11 @@
if(spsbox.files.length === 0) { if(spsbox.files.length === 0) {
return; return;
} }
spsbox.files[0].arrayBuffer().then(function(d){ GlobalState.fileReader.readAsArrayBuffer(spsbox.files[0]);
GlobalState.fileReader.onloadend = function() {
GlobalState.fileReader.onloadend = void(0);
try { try {
let vFilesystem = readSharkXPortSxpsFile(d); let vFilesystem = readSharkXPortSxpsFile(GlobalState.fileReader.result);
let output = readPS2D(vFilesystem[vFilesystem.rootDirectory]["icon.sys"].data); let output = readPS2D(vFilesystem[vFilesystem.rootDirectory]["icon.sys"].data);
updateDisplay(output); updateDisplay(output);
let output2 = new Object(); let output2 = new Object();
@ -544,8 +571,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 = `${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("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 = `${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}`; 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")) {
@ -556,9 +583,10 @@
} catch(e) { } catch(e) {
if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);} if(glBgContext!==null){glBgContext.clear(glBgContext.COLOR_BUFFER_BIT);}
if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);} if(glFgContext!==null){glFgContext.clear(glFgContext.COLOR_BUFFER_BIT | glFgContext.DEPTH_BUFFER_BIT);}
console.error(e);
alert(e); alert(e);
} }
}); };
} }
function createShader(gl, type, source) { function createShader(gl, type, source) {
let shader = gl.createShader(type); let shader = gl.createShader(type);
@ -639,7 +667,7 @@
<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.6.6"; document.getElementById("clientVersion").textContent = "0.6.7";
document.getElementById("currentYear").textContent = (new Date()).getFullYear().toString(); document.getElementById("currentYear").textContent = (new Date()).getFullYear().toString();
</script> </script>
</body> </body>