158 lines
4.9 KiB
Plaintext
158 lines
4.9 KiB
Plaintext
//
|
|
// nsbtx.js
|
|
//--------------------
|
|
// Reads NSBTX files (or TEX0 sections) and provides canvases containing decoded texture data.
|
|
// by RHY3756547
|
|
//
|
|
// includes: gl-matrix.js (glMatrix 2.0)
|
|
// /formats/nitro.js
|
|
//
|
|
|
|
window.nsbtx = function(input, tex0, autogen) {
|
|
var texDataSize, texInfoOff, texOffset, compTexSize, compTexInfoOff,
|
|
compTexOffset, compTexInfoDataOff /*wtf*/, palSize, palInfoOff,
|
|
palOffset, mainOff
|
|
|
|
var textureInfo, paletteInfo, palData, texData, colourBuffer
|
|
var thisObj = this;
|
|
|
|
var bitDepths = [0, 8, 2, 4, 8, 2, 8, 16]
|
|
|
|
if (input != null) {
|
|
load(input, tex0, autogen);
|
|
}
|
|
this.load = load;
|
|
this.readTexWithPal = readTexWithPal;
|
|
|
|
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
|
|
|
function load(input, tex0, autogen) {
|
|
var colourBuffer = new Uint32Array(4);
|
|
var view = new DataView(input);
|
|
var header = null;
|
|
var offset = 0;
|
|
if (!tex0) { //nitro 3d header
|
|
header = nitro.readHeader(view);
|
|
if (header.stamp != "BTX0") throw "NSBTX invalid. Expected BTX0, found "+header.stamp;
|
|
if (header.numSections > 1) throw "NSBTX invalid. Too many sections - should only have one.";
|
|
offset = header.sectionOffsets[0];
|
|
}
|
|
|
|
mainOff = offset;
|
|
|
|
var stamp = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
|
if (stamp != "TEX0") throw "NSBTX invalid. Expected TEX0, found "+stamp;
|
|
var size = view.getUint32(offset+0x04, true);
|
|
var texDataSize = view.getUint16(offset+0x0C, true)<<8;
|
|
var texInfoOff = view.getUint16(offset+0x0E, true);
|
|
var texOffset = view.getUint16(offset+0x14, true);
|
|
|
|
var compTexSize = view.getUint16(offset+0x1C, true)<<8;
|
|
var compTexInfoOff = view.getUint16(offset+0x1E, true);
|
|
var compTexOffset = view.getUint32(offset+0x24, true);
|
|
var compTexInfoDataOff = view.getUint32(offset+0x28, true);
|
|
|
|
var palSize = view.getUint32(offset+0x30, true)<<3;
|
|
var palInfoOff = view.getUint32(offset+0x34, true);
|
|
var palOffset = view.getUint32(offset+0x38, true);
|
|
|
|
//read palletes, then textures.
|
|
palData = input.slice(mainOff + palOffset, palSize);
|
|
texData = input.slice(mainOff + texOffset, texDataSize);
|
|
|
|
paletteInfo = nitro.read3dInfo(view, palInfoOff, palInfoHandler);
|
|
textureInfo = nitro.read3dInfo(view, texInfoOff, texInfoHandler);
|
|
|
|
if (false) {
|
|
console.log(textureInfo.objectData.length)
|
|
for (var i=0; i<textureInfo.objectData.length; i++) {
|
|
var canvas = readTexWithPal(i, textureInfo.objectData[i].pal)
|
|
document.body.appendChild(canvas);
|
|
console.log("uh")
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
function readTexWithPal(textureId, palId) {
|
|
palView = new DataView(palData);
|
|
texView = new DataView(texData);
|
|
var tex = textureInfo.objectData[textureId];
|
|
var pal = paletteInfo.objectData[palId];
|
|
|
|
var bit = bitDepths[pal.format]; //todo, compressed format whatever that is
|
|
|
|
var canvas = document.createElement("canvas");
|
|
canvas.width = tex.width;
|
|
canvas.height = tex.height;
|
|
var ctx = canvas.getContext("2d");
|
|
var data = ctx.getImageData(0, 0, tex.width, tex.height);
|
|
|
|
var off = tex.texOffset
|
|
var palOff = pal.palOffset;
|
|
|
|
var total = tex.width*tex.height;
|
|
var databuf = texView.readUint8(off);
|
|
for (var i=0; i<total; i++) {
|
|
var col;
|
|
if (bitDepths == 2) {
|
|
col = readPalColour(palView, palOff, (databuf>>((i%4)*2))&3)
|
|
if (i%4 == 3) databuf = texView.readUint8(++off);
|
|
} else if (bitDepths == 4) {
|
|
if (i%2 == 0) {
|
|
col = readPalColour(palView, palOff, databuf&15)
|
|
} else {
|
|
col = readPalColour(palView, palOff, databuf>>4)
|
|
databuf = texView.readUint8(++off);
|
|
}
|
|
} else if (bitDepths == 8) {
|
|
col = readPalColour(palView, palOff, texView.readUint8(off))
|
|
off += 1;
|
|
} else if (bitDepths == 16) {
|
|
col = readPalColour(palView, palOff, texView.readUint16(off, true))
|
|
off += 2;
|
|
}
|
|
data.data.set(col, i*4);
|
|
}
|
|
return canvas;
|
|
}
|
|
|
|
function readPalColour(view, palOff, ind) {
|
|
var col = palView.getUint16(palOff+ind*2, true);
|
|
colourBuffer[0] = Math.round(((col&31)/31)*255)
|
|
colourBuffer[1] = Math.round((((col>>5)&31)/31)*255)
|
|
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
|
colourBuffer[3] = Math.round((col>>15)*255)
|
|
}
|
|
|
|
function palInfoHandler(view, offset) {
|
|
var palOffset = view.getUint16(offset, true)<<3;
|
|
var unknown = view.getUint16(offset+2, true);
|
|
return {
|
|
palOffset: palOffset,
|
|
nextoff: offset+4
|
|
}
|
|
}
|
|
|
|
function texInfoHandler(view, offset) {
|
|
var texOffset = view.getUint16(offset, true)<<3;
|
|
var flags = view.getUint16(offset+2, true);
|
|
var width2 = view.getUint8(offset+4, true);
|
|
var unknown = view.getUint8(offset+5, true);
|
|
var height2 = view.getUint8(offset+6, true);
|
|
var unknown2 = view.getUint8(offset+7, true);
|
|
return {
|
|
texOffset: texOffset,
|
|
pal: (flags>>13),
|
|
format: ((flags>>10)&7),
|
|
height: ((flags>>7)&7)<<3,
|
|
width: ((flags>>4)&7)<<3,
|
|
|
|
nextoff: offset+8
|
|
}
|
|
}
|
|
|
|
function readChar(view, offset) {
|
|
return String.fromCharCode(view.getUint8(offset));
|
|
}
|
|
} |