Looks like I have to clean this out...
This commit is contained in:
1 parent
7e213690c8
commit
a16ba9a907
192 files changed
-40154
No files matched your search
@@ -1,158 +0,0 @@
|
||||
//
|
||||
// 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 (f) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
//
|
||||
// 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 (autogen) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
//
|
||||
// netKart.js
|
||||
//--------------------
|
||||
// Singleton for serializing and restoring kart data.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /entities/kart.js
|
||||
//
|
||||
|
||||
window.netKart = new function() {
|
||||
var animNames = ["drive", "win", "lose", "spin"]
|
||||
this.saveKart = saveKart;
|
||||
this.restoreKart = restoreKart;
|
||||
|
||||
function saveKart(view, off, k, input) { // requires 0x60 bytes of space from the offset location
|
||||
saveVec3(view, off, k.pos);
|
||||
saveVec3(view, off+0xC, k.vel);
|
||||
view.setFloat32(off+0x18, k.angle, true);
|
||||
view.setFloat32(off+0x1C, k.physicalDir, true);
|
||||
view.setFloat32(off+0x20, k.driftOff, true);
|
||||
if (k.cannon != null) view.setUint16(off+0x24, k.cannon, true);
|
||||
else view.setUint16(off+0x24, 0xFFFF, true);
|
||||
|
||||
view.setUint16(off+0x26, k.airTime, true);
|
||||
view.setUint16(off+0x28, k.lastCollided, true);
|
||||
|
||||
view.setUint8(off+0x2A, k.boostMT);
|
||||
view.setUint8(off+0x2B, k.boostNorm);
|
||||
|
||||
view.setUint16(off+0x2C, k.stuckTo, true);
|
||||
view.setUint8(off+0x2E, k.wheelTurn);
|
||||
|
||||
saveVec3(view, off+0x30, k.kartColVel);
|
||||
view.setUint8(off+0x3C, k.kartColTimer);
|
||||
|
||||
saveVec3(view, off+0x3D, k.kartTargetNormal);
|
||||
saveVec3(view, off+0x49, k.trackAttach);
|
||||
|
||||
var driftFlags = ((k.drifting)?1:0)|(k.driftMode<<1)|((k.driftLanded)?8:0);
|
||||
view.setUint8(off+0x55, driftFlags);
|
||||
|
||||
view.setUint8(off+0x56, animNames.indexOf(k.animMode));
|
||||
|
||||
var binput = ((input.accel)?1:0)|((input.decel)?2:0)|((input.drift)?4:0);
|
||||
view.setUint8(off+0x57, binput);
|
||||
|
||||
view.setFloat32(off+0x58, input.turn, true);
|
||||
view.setFloat32(off+0x5C, input.airTurn, true);
|
||||
}
|
||||
|
||||
function restoreKart(view, off, k) { // we use the same endianness format as the ds to avoid confusion.
|
||||
try {
|
||||
k.pos = readVec3(view, off, k.pos);
|
||||
k.vel = readVec3(view, off+0xC, k.vel);
|
||||
k.angle = view.getFloat32(off+0x18, true);
|
||||
k.physicalDir = view.getFloat32(off+0x1C, true);
|
||||
k.driftOff = view.getFloat32(off+0x20, true);
|
||||
k.cannon = view.getUint16(off+0x24, true);
|
||||
if (k.cannon == 0xFFFF) k.cannon = null;
|
||||
|
||||
k.airTime = view.getUint16(off+0x26, true);
|
||||
k.lastCollided = view.getUint16(off+0x28, true);
|
||||
|
||||
k.boostMT = view.getUint8(off+0x2A);
|
||||
k.boostNorm = view.getUint8(off+0x2B);
|
||||
|
||||
k.stuckTo = view.getUint16(off+0x2C, true);
|
||||
k.wheelTurn = view.getUint8(off+0x2E);
|
||||
|
||||
k.kartColVel = readVec3(view, off+0x30, k.kartColVel);
|
||||
k.kartColTimer = view.getUint8(off+0x3C);
|
||||
|
||||
k.kartTargetNormal = readVec3(view, off+0x3D, k.kartTargetNormal);
|
||||
k.trackAttach = readVec3(view, off+0x49, k.trackAttach);
|
||||
|
||||
var driftFlags = view.getUint8(off+0x55);
|
||||
|
||||
k.drifting = driftFlags&1;
|
||||
k.driftMode = (driftFlags>>1)&3;
|
||||
k.driftLanded = driftFlags&8;
|
||||
|
||||
k.animMode = animNames[view.getUint8(off+0x56)];
|
||||
|
||||
k.controller.binput = view.getUint8(off+0x57);
|
||||
|
||||
k.controller.turn = view.getFloat32(off+0x58, true);
|
||||
k.controller.airTurn = view.getFloat32(off+0x5C, true);
|
||||
|
||||
} catch (err) {
|
||||
console.err("Kart restore failure:"+err.message);
|
||||
//failed to restore kart data. may wish to disconnect on this, but it's probably better to not react.
|
||||
}
|
||||
}
|
||||
|
||||
function saveVec3(view, off, vec) {
|
||||
var vec = vec;
|
||||
if (vec == null) vec = [NaN, NaN, NaN];
|
||||
view.setFloat32(off, vec[0], true);
|
||||
view.setFloat32(off+4, vec[1], true);
|
||||
view.setFloat32(off+8, vec[2], true);
|
||||
}
|
||||
|
||||
function readVec3(view, off, vec) {
|
||||
var first = view.getFloat32(off, true);
|
||||
if (isNaN(first)) return null;
|
||||
vec = vec3.create();
|
||||
vec[0] = first;
|
||||
vec[1] = view.getFloat32(off+4, true);
|
||||
vec[2] = view.getFloat32(off+8, true);
|
||||
return vec;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//
|
||||
// kartoffsetdata.js
|
||||
//--------------------
|
||||
// Provides functionality to read mario kart ds kart wheel and character model offsets.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.kartoffsetdata = function(input) {
|
||||
|
||||
var thisObj = this;
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
var off = 0;
|
||||
var karts = []
|
||||
for (var i=0; i<37; i++) {
|
||||
var obj = {};
|
||||
obj.name = readString(view, off, 0x10);
|
||||
off += 0x10;
|
||||
obj.frontTireSize = view.getInt32(off, true)/4096;
|
||||
off += 4;
|
||||
|
||||
var wheels = [];
|
||||
for (var j=0; j<4; j++) {
|
||||
var pos = vec3.create();
|
||||
pos[0] = view.getInt32(off, true)/4096;
|
||||
pos[1] = view.getInt32(off+4, true)/4096;
|
||||
pos[2] = view.getInt32(off+8, true)/4096;
|
||||
off += 12;
|
||||
wheels.push(pos);
|
||||
}
|
||||
|
||||
var chars = [];
|
||||
for (var j=0; j<13; j++) {
|
||||
var pos = vec3.create();
|
||||
pos[0] = view.getInt32(off, true)/4096;
|
||||
pos[1] = view.getInt32(off+4, true)/4096;
|
||||
console.log("charPos: "+pos[1]);
|
||||
pos[2] = view.getInt32(off+8, true)/4096;
|
||||
off += 12;
|
||||
chars.push(pos);
|
||||
}
|
||||
|
||||
obj.wheels = wheels;
|
||||
obj.chars = chars;
|
||||
|
||||
karts.push(obj);
|
||||
}
|
||||
thisObj.karts = karts;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
function readString(view, offset, length) {
|
||||
var str = "";
|
||||
for (var i=0; i<length; i++) {
|
||||
var b = view.getUint8(offset++);
|
||||
if (b != 0) str += String.fromCharCode(b);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
//
|
||||
// kartphysicalparam.js
|
||||
//--------------------
|
||||
// Provides functionality to read mario kart ds kart physical parameters
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0) (maybe)
|
||||
//
|
||||
|
||||
window.kartphysicalparam = function(input) {
|
||||
|
||||
var thisObj = this;
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
var off = 0;
|
||||
var karts = []
|
||||
for (var i=0; i<50; i++) {
|
||||
var obj = {};
|
||||
var colParam = [];
|
||||
|
||||
obj.colRadius = view.getInt32(off, true)/4096;
|
||||
obj.unknown1 = view.getInt32(off+0x4, true)/4096;
|
||||
obj.unknown2 = view.getInt32(off+0x8, true)/4096;
|
||||
obj.weight = view.getInt16(off+0xC, true)/4096;
|
||||
obj.miniTurbo = view.getUint16(off+0xE, true);
|
||||
obj.topSpeed = view.getInt32(off+0x10, true)/4096;
|
||||
obj.accel1 = view.getInt32(off+0x14, true)/4096;
|
||||
obj.accel2 = view.getInt32(off+0x18, true)/4096;
|
||||
obj.accelSwitch = view.getInt32(off+0x1C, true)/4096;
|
||||
obj.driftAccel1 = view.getInt32(off+0x20, true)/4096;
|
||||
obj.driftAccel2 = view.getInt32(off+0x24, true)/4096;
|
||||
obj.driftAccelSwitch = view.getInt32(off+0x28, true)/4096;
|
||||
obj.decel = view.getInt32(off+0x2C, true)/4096;
|
||||
obj.turnRate = (view.getInt16(off+0x30, true)/32768)*Math.PI;
|
||||
obj.driftTurnRate = (view.getInt16(off+0x32, true)/32768)*Math.PI;
|
||||
obj.driftOffRestore = (view.getInt16(off+0x34, true)/32768)*Math.PI;
|
||||
obj.unknown3 = view.getInt16(off+0x36, true);
|
||||
|
||||
var off1 = off+0x38;
|
||||
var off2 = off+0x68;
|
||||
for (var j=0; j<12; j++) {
|
||||
var handling = view.getInt32(off1, true)/4096;
|
||||
var topSpeed = view.getInt32(off2, true)/4096;
|
||||
colParam.push({
|
||||
handling: handling,
|
||||
topSpeedMul: topSpeed
|
||||
});
|
||||
off1+=4;
|
||||
off2+=4;
|
||||
}
|
||||
|
||||
obj.colParam = colParam;
|
||||
|
||||
karts.push(obj);
|
||||
off += 0x98;
|
||||
}
|
||||
thisObj.karts = karts;
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
//
|
||||
// kcl.js
|
||||
//--------------------
|
||||
// Loads kcl files and provides a variety of functions for accessing and using the data.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.kcl = function(input, mkwii) {
|
||||
//todo, support versions for other games (MKWii etc)
|
||||
|
||||
this.load = load;
|
||||
this.getPlanesAt = getPlanesAt;
|
||||
|
||||
var vertexOffset, normalOffset, planeOffset, octreeOffset, unknown1, topLeftVec,
|
||||
xMask, yMask, zMask, coordShift, yShift, zShift, unknown2, trisMapped = 0,
|
||||
//decoded data
|
||||
planes, octree, end, mkwiiMode //little endian for ds, big endian for wii
|
||||
|
||||
var sf, mouseX = 0, mouseY = 0, offx, offz, loaded = false //for testing
|
||||
var Fixed32Point = 4096;
|
||||
|
||||
if (input != null) {
|
||||
//handle input, load kcl from data
|
||||
if (typeof input == "string") {
|
||||
var xml = new XMLHttpRequest();
|
||||
xml.responseType = "arraybuffer";
|
||||
xml.open("GET", input, true);
|
||||
xml.onload = function() {
|
||||
load(xml.response);
|
||||
}
|
||||
xml.send();
|
||||
} else {
|
||||
load(input, mkwii);
|
||||
}
|
||||
}
|
||||
|
||||
window.onmousemove = function(evt) {
|
||||
mouseX = evt.pageX;
|
||||
mouseY = evt.pageY;
|
||||
}
|
||||
|
||||
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
||||
//canvas = document.getElementById("canvas");
|
||||
//ctx = canvas.getContext("2d");
|
||||
//setInterval(render, 16);
|
||||
|
||||
function readBigDec(view, off, mkwii) {
|
||||
if (mkwii) return view.getFloat32(off);
|
||||
else return view.getInt32(off, end)/Fixed32Point;
|
||||
}
|
||||
|
||||
function load(buffer, mkwii) {
|
||||
var mkwii = mkwii;
|
||||
if (mkwii == null) mkwii = false;
|
||||
end = !mkwii;
|
||||
mkwiiMode = mkwii;
|
||||
var time = Date.now();
|
||||
//loads kcl from an array buffer.
|
||||
var view = new DataView(buffer);
|
||||
vertexOffset = view.getUint32(0x00, end);
|
||||
normalOffset = view.getUint32(0x04, end);
|
||||
planeOffset = view.getUint32(0x08, end);
|
||||
octreeOffset = view.getUint32(0x0C, end);
|
||||
unknown1 = readBigDec(view, 0x10, mkwii);
|
||||
var vec = vec3.create();
|
||||
vec[0] = readBigDec(view, 0x14, mkwii);
|
||||
vec[1] = readBigDec(view, 0x18, mkwii);
|
||||
vec[2] = readBigDec(view, 0x1C, mkwii);
|
||||
topLeftVec = vec;
|
||||
xMask = view.getUint32(0x20, end);
|
||||
yMask = view.getUint32(0x24, end);
|
||||
zMask = view.getUint32(0x28, end);
|
||||
coordShift = view.getUint32(0x2C, end);
|
||||
yShift = view.getUint32(0x30, end);
|
||||
zShift = view.getUint32(0x34, end);
|
||||
unknown2 = readBigDec(view, 0x38, mkwii);
|
||||
|
||||
//read planes, there should be as many as there is 16 byte spaces between planeOffset+0x10 and octreeOffset
|
||||
offset = planeOffset+0x10;
|
||||
planes = [null]; //0 index is empty
|
||||
var minx=0, maxx=0, minz=0, maxz=0;
|
||||
while (offset < octreeOffset) {
|
||||
planes.push(new Plane(view, offset));
|
||||
offset += 0x10;
|
||||
var vert = planes[planes.length-1].Vertex1;
|
||||
if (vert[0] < minx) minx=vert[0];
|
||||
if (vert[0] > maxx) maxx=vert[0];
|
||||
if (vert[2] < minz) minz=vert[2];
|
||||
if (vert[2] > maxz) maxz=vert[2];
|
||||
}
|
||||
|
||||
console.log("minx: "+minx+" maxx: "+maxx+" minz: "+minz+" maxz: "+maxz)
|
||||
|
||||
//var sfx = canvas.width/(maxx-minx);
|
||||
//var sfy = canvas.height/(maxz-minz);
|
||||
//offx = -((minx+maxx)/2);
|
||||
//offz = -((minz+maxz)/2);
|
||||
//sf = Math.min(sfx, sfy)*0.8;
|
||||
octree = []
|
||||
|
||||
var rootNodes = ((~xMask >> coordShift) + 1) * ((~yMask >> coordShift) + 1) * ((~zMask >> coordShift) + 1);
|
||||
|
||||
for (var i=0; i<rootNodes; i++) {
|
||||
var off = octreeOffset+i*4;
|
||||
octree.push(decodeCube(octreeOffset, off, view));
|
||||
}
|
||||
loaded = true;
|
||||
//alert("process took "+(Date.now()-time)+"ms");
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!loaded) return;
|
||||
ctx.save();
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.translate(canvas.width/2, canvas.height/2);
|
||||
ctx.scale(sf, sf);
|
||||
ctx.translate(offx, offz);
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.lineWidth = 5;
|
||||
testDrawPlanes(planes);
|
||||
|
||||
var test = getPlanesAt(((mouseX-canvas.width/2)/sf)-offx, topLeftVec[1]+Math.random()*(~yMask), ((mouseY-canvas.height/2)/sf)-offz);
|
||||
ctx.strokeStyle = "#FF0000";
|
||||
ctx.lineWidth = 20;
|
||||
testDrawPlanes(test);
|
||||
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.lineWidth = 3;
|
||||
drawOctreeBorders();
|
||||
|
||||
ctx.restore();
|
||||
|
||||
/*if (test5 != null) {
|
||||
ctx.lineWidth = 0.01;
|
||||
ctx.save();
|
||||
ctx.translate(canvas.width/2, canvas.height/2);
|
||||
ctx.scale(sf, sf);
|
||||
ctx.translate(offx, offz);
|
||||
ctx.scale(1024, 1024);
|
||||
|
||||
ctx.strokeStyle = "#0000FF";
|
||||
var mdl = test5.modelBuffers[0];
|
||||
for (var i=0; i<mdl.length; i++) {
|
||||
var strip = mdl[i].strips[0];
|
||||
var off = 0;
|
||||
for (var j=0; j<strip.verts/3; j++) {
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.moveTo(strip.posArray[off], strip.posArray[off+2]);
|
||||
ctx.lineTo(strip.posArray[off+3], strip.posArray[off+5]);
|
||||
ctx.lineTo(strip.posArray[off+6], strip.posArray[off+8]);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
off += 9;
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
function drawOctreeBorders() {
|
||||
var size = 1<<coordShift;
|
||||
for (var x=0; x<((~xMask >> coordShift) + 1); x++) {
|
||||
for (var z=0; z<((~zMask >> coordShift) + 1); z++) {
|
||||
ctx.strokeRect(topLeftVec[0]+size*x, topLeftVec[2]+size*z, size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testDrawPlanes(planes) {
|
||||
for (var i=1; i<planes.length; i++) {
|
||||
var plane = planes[i];
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.moveTo(plane.Vertex1[0], plane.Vertex1[2]);
|
||||
ctx.lineTo(plane.Vertex2[0], plane.Vertex2[2]);
|
||||
ctx.lineTo(plane.Vertex3[0], plane.Vertex3[2]);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getPlanesAt(x, y, z) {
|
||||
x -= topLeftVec[0];
|
||||
y -= topLeftVec[1];
|
||||
z -= topLeftVec[2];
|
||||
if (x<0 || y<0 || z<0) return []; //no collision
|
||||
else {
|
||||
x = Math.floor(x);
|
||||
y = Math.floor(y);
|
||||
z = Math.floor(z);
|
||||
if ((x&xMask)>0 || (y&yMask)>0 || (z&zMask)>0) return []; //no collision
|
||||
|
||||
var index = (x>>coordShift)|((y>>coordShift)<<yShift)|((z>>coordShift)<<zShift)
|
||||
return traverseOctree(octree[index], x, y, z, coordShift-1);
|
||||
}
|
||||
}
|
||||
|
||||
function traverseOctree(node, x, y, z, shift) {
|
||||
if (node.leaf) return node.realTris;
|
||||
//otherwise we're a node! find next index and traverse
|
||||
var index = ((x>>shift)&1)|(((y>>shift)&1)<<1)|(((z>>shift)&1)<<2);
|
||||
return traverseOctree(node.items[index], x, y, z, shift-1);
|
||||
}
|
||||
|
||||
function decodeCube(baseoff, off, view) {
|
||||
var data = view.getUint32(off, end);
|
||||
var off2 = baseoff+(data&0x7FFFFFFF);
|
||||
if (off2 >= view.byteLength) {
|
||||
return {
|
||||
leaf: true,
|
||||
tris: [],
|
||||
realTris: []
|
||||
}
|
||||
}
|
||||
if (data&0x80000000) { //is a leaf.
|
||||
off2 += 2;
|
||||
var tris = [];
|
||||
var realTris = [];
|
||||
while (true) {
|
||||
var read = view.getUint16(off2, end);
|
||||
if (read == 0) break; //zero terminated
|
||||
tris.push(read);
|
||||
realTris.push(planes[read]);
|
||||
trisMapped += 1;
|
||||
off2 += 2;
|
||||
}
|
||||
return {
|
||||
leaf: true,
|
||||
tris: tris,
|
||||
realTris: realTris
|
||||
}
|
||||
} else { //contains 8 more cubes
|
||||
var cubes = [];
|
||||
var boff = off2;
|
||||
for (var i=0; i<8; i++) {
|
||||
cubes.push(decodeCube(boff, off2, view));
|
||||
off2 += 4;
|
||||
}
|
||||
return {
|
||||
leaf: false,
|
||||
items: cubes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Plane(view, offset) {
|
||||
this.Len = readBigDec(view, offset, mkwiiMode);
|
||||
this.Vertex1 = readVert(view.getUint16(offset+0x4, end), view);
|
||||
this.Normal = readNormal(view.getUint16(offset+0x6, end), view);
|
||||
this.NormalA = readNormal(view.getUint16(offset+0x8, end), view);
|
||||
this.NormalB = readNormal(view.getUint16(offset+0xA, end), view);
|
||||
this.NormalC = readNormal(view.getUint16(offset+0xC, end), view);
|
||||
this.CollisionType = view.getUint16(offset+0xE, end);
|
||||
|
||||
var crossA = vec3.cross(vec3.create(), this.NormalA, this.Normal);
|
||||
var crossB = vec3.cross(vec3.create(), this.NormalB, this.Normal);
|
||||
|
||||
this.Vertex2 = vec3.scaleAndAdd(vec3.create(), this.Vertex1, crossB, (this.Len/vec3.dot(crossB, this.NormalC)));
|
||||
this.Vertex3 = vec3.scaleAndAdd(vec3.create(), this.Vertex1, crossA, (this.Len/vec3.dot(crossA, this.NormalC)));
|
||||
}
|
||||
|
||||
function readVert(num, view) {
|
||||
var vec = vec3.create();
|
||||
var loc = vertexOffset+num*0xC;
|
||||
vec[0] = readBigDec(view, loc, mkwiiMode);
|
||||
vec[1] = readBigDec(view, loc+0x4, mkwiiMode);
|
||||
vec[2] = readBigDec(view, loc+0x8, mkwiiMode);
|
||||
return vec;
|
||||
}
|
||||
|
||||
function readNormal(num, view) {
|
||||
var mkwii = mkwiiMode;
|
||||
var vec = vec3.create();
|
||||
if (mkwii) {
|
||||
var loc = normalOffset+num*0xC;
|
||||
vec[0] = view.getFloat32(loc);
|
||||
vec[1] = view.getFloat32(loc+0x4);
|
||||
vec[2] = view.getFloat32(loc+0x8);
|
||||
} else {
|
||||
var loc = normalOffset+num*0x6;
|
||||
vec[0] = view.getInt16(loc, end)/4096; //fixed point
|
||||
vec[1] = view.getInt16(loc+0x2, end)/4096;
|
||||
vec[2] = view.getInt16(loc+0x4, end)/4096;
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// lz77.js
|
||||
//--------------------
|
||||
// Reads and decompresses lz77 (mode 0x10 only) files. In future may be able to recompress.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.lz77 = new (function() {
|
||||
this.decompress = function(buffer) {
|
||||
var view = new DataView(buffer);
|
||||
var compType = view.getUint8(0);
|
||||
var size = view.getUint32(0, true)>>8;
|
||||
|
||||
var targ = new ArrayBuffer(size);
|
||||
var targA = new Uint8Array(targ);
|
||||
|
||||
var off = 4;
|
||||
var dOff = 0;
|
||||
var eof = buffer.byteLength;
|
||||
while (off<eof) {
|
||||
var flag = view.getUint8(off++);
|
||||
for (var j=7; j>=0; j--) {
|
||||
if (off>=eof) break;
|
||||
if ((flag>>j)&1) { //1=compressed, 2=raw byte
|
||||
var dat = view.getUint16(off);
|
||||
off += 2;
|
||||
var cOff = (dOff-(dat&4095))-1;
|
||||
var len = (dat>>12)+3;
|
||||
|
||||
for (var k=0; k<len; k++) {
|
||||
targA[dOff++] = targA[cOff++];
|
||||
}
|
||||
|
||||
} else {
|
||||
targA[dOff++] = view.getUint8(off++);
|
||||
}
|
||||
}
|
||||
}
|
||||
return targ;
|
||||
}
|
||||
})();
|
||||
@@ -1,196 +0,0 @@
|
||||
//
|
||||
// narc.js
|
||||
//--------------------
|
||||
// Reads narc archives and provides access to files by directory structure.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.narc = function(input) {
|
||||
|
||||
this.load = load;
|
||||
this.getFile = getFile;
|
||||
|
||||
var arc = this;
|
||||
var handlers = [];
|
||||
|
||||
window.onmousemove = function(evt) {
|
||||
mouseX = evt.pageX;
|
||||
mouseY = evt.pageY;
|
||||
}
|
||||
|
||||
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
||||
|
||||
function load(buffer) {
|
||||
arc.buffer = buffer; //we will use this data in the future.
|
||||
|
||||
var view = new DataView(buffer);
|
||||
arc.stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
if (arc.stamp != "NARC") throw "File provided is not a NARC archive! Expected NARC, found "+arc.stamp+".";
|
||||
|
||||
arc.byteOrder = view.getUint16(0x4, true); //todo: check byte order and flip to little endian when necessary
|
||||
arc.version = view.getUint16(0x6, true);
|
||||
arc.size = view.getUint32(0x8, true);
|
||||
arc.headSize = view.getUint16(0xC, true);
|
||||
arc.numBlocks = view.getUint16(0xE, true);
|
||||
|
||||
var off = arc.headSize;
|
||||
|
||||
arc.sections = {};
|
||||
for (var i=0; i<arc.numBlocks; i++) {
|
||||
var section = readSection(view, off);
|
||||
arc.sections[section.type] = section;
|
||||
off = 4*Math.ceil(section.nextOff/4);
|
||||
}
|
||||
}
|
||||
|
||||
function readSection(view, off) {
|
||||
var obj = {};
|
||||
obj.type = readChar(view, off+0x0)+readChar(view, off+0x1)+readChar(view, off+0x2)+readChar(view, off+0x3);
|
||||
obj.size = view.getUint32(off+0x4, true);
|
||||
|
||||
if (handlers[obj.type] == null) throw "Unknown NARC section "+obj.type+"!";
|
||||
handlers[obj.type](view, off+0x8, obj);
|
||||
|
||||
obj.nextOff = off+obj.size;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function getFile(name) {
|
||||
var path = name.split("/");
|
||||
var start = (path[0] == "")?1:0; //fix dirs relative to root (eg "/hi/test.bin")
|
||||
|
||||
var table = arc.sections["BTNF"].directories;
|
||||
var curDir = table[0].entries; //root
|
||||
for (var i=start; i<path.length; i++) {
|
||||
var found = false;
|
||||
for (var j=0; j<curDir.length; j++) {
|
||||
if (curDir[j].name == path[i]) {
|
||||
if (curDir[j].dir) {
|
||||
found = true;
|
||||
curDir = table[curDir[j].id-0xF000].entries;
|
||||
break;
|
||||
} else {
|
||||
return readFileWithID(curDir[j].id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
console.error("File not found: "+name+", could not find "+path[i]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
console.error("Path is not a file: "+name);
|
||||
return null; //incomplete path; we ended on a directory, not a file!
|
||||
}
|
||||
|
||||
function readFileWithID(id) {
|
||||
var table = arc.sections["BTAF"].files;
|
||||
var file = table[id];
|
||||
var off = arc.sections["GMIF"].baseOff;
|
||||
if (file == null) {
|
||||
console.error("File ID invalid: "+id);
|
||||
return null;
|
||||
}
|
||||
return arc.buffer.slice(file.start+off, file.end+off);
|
||||
}
|
||||
|
||||
var handlers = {};
|
||||
handlers["BTAF"] = function(view, off, obj) {
|
||||
obj.numFiles = view.getUint16(off, true);
|
||||
obj.reserved = view.getUint16(off+0x2, true);
|
||||
obj.files = [];
|
||||
off += 4;
|
||||
for (var i=0; i<obj.numFiles; i++) {
|
||||
var fl = {}
|
||||
fl.start = view.getUint32(off, true);
|
||||
fl.end = view.getUint32(off+4, true);
|
||||
obj.files.push(fl);
|
||||
off += 8;
|
||||
}
|
||||
}
|
||||
|
||||
handlers["BTNF"] = function(view, off, obj) { //filename table - includes directories and filenames.
|
||||
var soff = off;
|
||||
obj.directories = [];
|
||||
//read root dir, then we know number of directories to read.
|
||||
var root = {};
|
||||
var dirOff = soff+view.getUint32(off, true);
|
||||
root.firstFile = view.getUint16(off+4, true);
|
||||
populateDir(view, dirOff, root);
|
||||
root.numDir = view.getUint16(off+6, true);
|
||||
|
||||
off += 8;
|
||||
obj.directories.push(root);
|
||||
|
||||
var n = root.numDir-1;
|
||||
for (var i=0; i<n; i++) {
|
||||
var dir = {};
|
||||
var dirOff = soff+view.getUint32(off, true);
|
||||
dir.firstFile = view.getUint16(off+4, true);
|
||||
populateDir(view, dirOff, dir);
|
||||
dir.parent = view.getUint16(off+6, true);
|
||||
|
||||
off += 8;
|
||||
obj.directories.push(dir);
|
||||
}
|
||||
}
|
||||
|
||||
handlers["GMIF"] = function(view, off, obj) {
|
||||
obj.baseOff = off;
|
||||
}
|
||||
|
||||
function populateDir(view, off, dir) {
|
||||
curFile = dir.firstFile;
|
||||
dir.entries = [];
|
||||
while (true) {
|
||||
var flag = view.getUint8(off++);
|
||||
var len = flag&127;
|
||||
if (! (flag&128)) { //file or end of dir
|
||||
if (len == 0) return;
|
||||
else {
|
||||
dir.entries.push({
|
||||
dir: false,
|
||||
id: curFile++,
|
||||
name: readString(view, off, len)
|
||||
})
|
||||
off += len;
|
||||
}
|
||||
} else {
|
||||
var dirID = view.getUint16(off+len, true);
|
||||
dir.entries.push({
|
||||
dir: true,
|
||||
id: dirID,
|
||||
name: readString(view, off, len)
|
||||
});
|
||||
off += len+2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function readString(view, off, length) {
|
||||
var str = "";
|
||||
for (var i=0; i<length; i++) {
|
||||
str += readChar(view, off++);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
if (typeof input == "string") {
|
||||
var xml = new XMLHttpRequest();
|
||||
xml.responseType = "arraybuffer";
|
||||
xml.open("GET", input, true);
|
||||
xml.onload = function() {
|
||||
load(xml.response);
|
||||
}
|
||||
xml.send();
|
||||
} else {
|
||||
load(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
//
|
||||
// ndsFS.js
|
||||
//--------------------
|
||||
// Reads nds roms using nitroFS and provides access to files by directory structure.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.ndsFS = function(input) {
|
||||
this.load = load;
|
||||
this.getFile = getFile;
|
||||
|
||||
var arc = this;
|
||||
var handlers = [];
|
||||
|
||||
window.onmousemove = function(evt) {
|
||||
mouseX = evt.pageX;
|
||||
mouseY = evt.pageY;
|
||||
}
|
||||
|
||||
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
||||
|
||||
function load(buffer) {
|
||||
arc.buffer = buffer; //we will use this data in the future.
|
||||
|
||||
var view = new DataView(buffer);
|
||||
arc.view = view;
|
||||
arc.sections = {};
|
||||
|
||||
arc.nameOff = view.getUint32(0x40, true);
|
||||
arc.fileOff = view.getUint32(0x48, true)
|
||||
|
||||
arc.sections["BTNF"] = {};
|
||||
handlers["BTNF"](view, arc.nameOff, arc.sections["BTNF"]) //file name table
|
||||
|
||||
/*arc.sections["BTAF"] = {};
|
||||
handlers["BTAF"](view, , arc.sections["BTAF"]) //file alloc table */
|
||||
}
|
||||
|
||||
function getFile(name) {
|
||||
var path = name.split("/");
|
||||
var start = (path[0] == "")?1:0; //fix dirs relative to root (eg "/hi/test.bin")
|
||||
|
||||
var table = arc.sections["BTNF"].directories;
|
||||
var curDir = table[0].entries; //root
|
||||
for (var i=start; i<path.length; i++) {
|
||||
var found = false;
|
||||
for (var j=0; j<curDir.length; j++) {
|
||||
if (curDir[j].name == path[i]) {
|
||||
if (curDir[j].dir) {
|
||||
found = true;
|
||||
curDir = table[curDir[j].id-0xF000].entries;
|
||||
break;
|
||||
} else {
|
||||
return readFileWithID(curDir[j].id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
console.error("File not found: "+name+", could not find "+path[i]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
console.error("Path is not a file: "+name);
|
||||
return null; //incomplete path; we ended on a directory, not a file!
|
||||
}
|
||||
|
||||
function readFileWithID(id) {
|
||||
var off = arc.fileOff+id*8;
|
||||
/*var table = arc.sections["BTAF"].files;
|
||||
var file = table[id];
|
||||
if (file == null) {
|
||||
console.error("File ID invalid: "+id);
|
||||
return null;
|
||||
}*/
|
||||
return arc.buffer.slice(arc.view.getUint32(off, true), arc.view.getUint32(off+4, true));
|
||||
}
|
||||
|
||||
var handlers = {};
|
||||
handlers["BTAF"] = function(view, off, obj) {
|
||||
obj.files = [];
|
||||
for (var i=0; i<obj.numFiles; i++) {
|
||||
var fl = {}
|
||||
fl.start = view.getUint32(off, true);
|
||||
fl.end = view.getUint32(off+4, true);
|
||||
obj.files.push(fl);
|
||||
off += 8;
|
||||
}
|
||||
}
|
||||
|
||||
handlers["BTNF"] = function(view, off, obj) { //filename table - includes directories and filenames.
|
||||
var soff = off;
|
||||
obj.directories = [];
|
||||
//read root dir, then we know number of directories to read.
|
||||
var root = {};
|
||||
var dirOff = soff+view.getUint32(off, true);
|
||||
root.firstFile = view.getUint16(off+4, true);
|
||||
populateDir(view, dirOff, root);
|
||||
root.numDir = view.getUint16(off+6, true);
|
||||
|
||||
off += 8;
|
||||
obj.directories.push(root);
|
||||
|
||||
var n = root.numDir-1;
|
||||
for (var i=0; i<n; i++) {
|
||||
var dir = {};
|
||||
var dirOff = soff+view.getUint32(off, true);
|
||||
dir.firstFile = view.getUint16(off+4, true);
|
||||
populateDir(view, dirOff, dir);
|
||||
dir.parent = view.getUint16(off+6, true);
|
||||
|
||||
off += 8;
|
||||
obj.directories.push(dir);
|
||||
}
|
||||
}
|
||||
|
||||
function populateDir(view, off, dir) {
|
||||
curFile = dir.firstFile;
|
||||
dir.entries = [];
|
||||
while (true) {
|
||||
var flag = view.getUint8(off++);
|
||||
var len = flag&127;
|
||||
if (! (flag&128)) { //file or end of dir
|
||||
if (len == 0) return;
|
||||
else {
|
||||
dir.entries.push({
|
||||
dir: false,
|
||||
id: curFile++,
|
||||
name: readString(view, off, len)
|
||||
})
|
||||
off += len;
|
||||
}
|
||||
} else {
|
||||
var dirID = view.getUint16(off+len, true);
|
||||
dir.entries.push({
|
||||
dir: true,
|
||||
id: dirID,
|
||||
name: readString(view, off, len)
|
||||
});
|
||||
off += len+2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function readString(view, off, length) {
|
||||
var str = "";
|
||||
for (var i=0; i<length; i++) {
|
||||
str += readChar(view, off++);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
if (typeof input == "string") {
|
||||
var xml = new XMLHttpRequest();
|
||||
xml.responseType = "arraybuffer";
|
||||
xml.open("GET", input, true);
|
||||
xml.onload = function() {
|
||||
load(xml.response);
|
||||
}
|
||||
xml.send();
|
||||
} else {
|
||||
load(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
//
|
||||
// netKart.js
|
||||
//--------------------
|
||||
// Singleton for serializing and restoring kart data.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /entities/kart.js
|
||||
//
|
||||
|
||||
window.netKart = new function() {
|
||||
var animNames = ["drive", "win", "lose", "spin"]
|
||||
this.saveKart = saveKart;
|
||||
this.restoreKart = restoreKart;
|
||||
|
||||
function saveKart(view, off, k, input) { // requires 0x60 bytes of space from the offset location
|
||||
saveVec3(view, off, k.pos);
|
||||
saveVec3(view, off+0xC, k.vel);
|
||||
view.setFloat32(off+0x18, k.angle, true);
|
||||
view.setFloat32(off+0x1C, k.physicalDir, true);
|
||||
view.setFloat32(off+0x20, k.driftOff, true);
|
||||
if (k.cannon != null) view.setUint16(off+0x24, k.cannon, true);
|
||||
else view.setUint16(off+0x24, 0xFFFF, true);
|
||||
|
||||
view.setUint16(off+0x26, k.airTime, true);
|
||||
view.setUint16(off+0x28, k.lastCollided, true);
|
||||
|
||||
view.setUint8(off+0x2A, k.boostMT);
|
||||
view.setUint8(off+0x2B, k.boostNorm);
|
||||
|
||||
view.setUint16(off+0x2C, k.stuckTo, true);
|
||||
view.setUint8(off+0x2E, k.wheelTurn);
|
||||
|
||||
saveVec3(view, off+0x30, k.kartColVel);
|
||||
view.setUint8(off+0x3C, k.kartColTimer);
|
||||
|
||||
saveVec3(view, off+0x3D, k.kartTargetNormal);
|
||||
saveVec3(view, off+0x49, k.trackAttach);
|
||||
|
||||
var driftFlags = ((k.drifting)?1:0)|(k.driftMode<<1)|((k.driftLanded)?8:0);
|
||||
view.setUint8(off+0x55, driftFlags);
|
||||
|
||||
view.setUint8(off+0x56, animNames.indexOf(k.animMode));
|
||||
|
||||
var binput = ((input.accel)?1:0)|((input.decel)?2:0)|((input.drift)?4:0);
|
||||
view.setUint8(off+0x57, binput);
|
||||
|
||||
view.setFloat32(off+0x58, input.turn, true);
|
||||
view.setFloat32(off+0x5C, input.airTurn, true);
|
||||
}
|
||||
|
||||
function restoreKart(view, off, k) { // we use the same endianness format as the ds to avoid confusion.
|
||||
try {
|
||||
k.pos = readVec3(view, off, k.pos);
|
||||
k.vel = readVec3(view, off+0xC, k.vel);
|
||||
k.angle = view.getFloat32(off+0x18, true);
|
||||
k.physicalDir = view.getFloat32(off+0x1C, true);
|
||||
k.driftOff = view.getFloat32(off+0x20, true);
|
||||
k.cannon = view.getUint16(off+0x24, true);
|
||||
if (k.cannon == 0xFFFF) k.cannon = null;
|
||||
|
||||
k.airTime = view.getUint16(off+0x26, true);
|
||||
k.lastCollided = view.getUint16(off+0x28, true);
|
||||
|
||||
k.boostMT = view.getUint8(off+0x2A);
|
||||
k.boostNorm = view.getUint8(off+0x2B);
|
||||
|
||||
k.stuckTo = view.getUint16(off+0x2C, true);
|
||||
k.wheelTurn = view.getUint8(off+0x2E);
|
||||
|
||||
k.kartColVel = readVec3(view, off+0x30, k.kartColVel);
|
||||
k.kartColTimer = view.getUint8(off+0x3C);
|
||||
|
||||
k.kartTargetNormal = readVec3(view, off+0x3D, k.kartTargetNormal);
|
||||
k.trackAttach = readVec3(view, off+0x49, k.trackAttach);
|
||||
|
||||
var driftFlags = view.getUint8(off+0x55);
|
||||
|
||||
k.drifting = driftFlags&1;
|
||||
k.driftMode = (driftFlags>>1)&3;
|
||||
k.driftLanded = driftFlags&8;
|
||||
|
||||
k.animMode = animNames[view.getUint8(off+0x56)];
|
||||
|
||||
k.controller.binput = view.getUint8(off+0x57);
|
||||
|
||||
k.controller.turn = view.getFloat32(off+0x58, true);
|
||||
k.controller.airTurn = view.getFloat32(off+0x5C, true);
|
||||
|
||||
} catch (err) {
|
||||
console.err("Kart restore failure:"+err.message);
|
||||
//failed to restore kart data. may wish to disconnect on this, but it's probably better to not react.
|
||||
}
|
||||
}
|
||||
|
||||
function saveVec3(view, off, vec) {
|
||||
var vec = vec;
|
||||
if (vec == null) vec = [NaN, NaN, NaN];
|
||||
view.setFloat32(off, vec[0], true);
|
||||
view.setFloat32(off+4, vec[1], true);
|
||||
view.setFloat32(off+8, vec[2], true);
|
||||
}
|
||||
|
||||
function readVec3(view, off, vec) {
|
||||
var first = view.getFloat32(off, true);
|
||||
if (isNaN(first)) return null;
|
||||
vec = vec3.create();
|
||||
vec[0] = first;
|
||||
vec[1] = view.getFloat32(off+4, true);
|
||||
vec[2] = view.getFloat32(off+8, true);
|
||||
return vec;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// nftr.js
|
||||
//--------------------
|
||||
// Reads NFTR fonts and compiles them to a texture and character lookup table. Texture is replaceable.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /formats/nitro.js
|
||||
//
|
||||
|
||||
window.nftr = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var mainObj = this;
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
var tex;
|
||||
|
||||
//nitro 3d header
|
||||
header = nitro.readHeader(view);
|
||||
//debugger;
|
||||
if (header.stamp != "RTFN") throw "NFTR invalid. Expected RTFN, found "+header.stamp;
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
|
||||
mainOff = offset;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
//
|
||||
// nitro.js
|
||||
//--------------------
|
||||
// General purpose functions for nitro formats, eg. NSBTX or NSBMD
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.nitro = new function() {
|
||||
this.readHeader = function(view) { //input: DataView with base offset at header position
|
||||
var stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
var unknown1 = view.getUint32(0x4, true);
|
||||
var filesize = view.getUint32(0x8, true);
|
||||
var headsize = view.getUint16(0xC, true);
|
||||
var numSections = view.getUint16(0xE, true);
|
||||
var sectionOffsets = [];
|
||||
for (var i=0; i<numSections; i++) {
|
||||
sectionOffsets.push(view.getUint32(0x10+i*4, true));
|
||||
}
|
||||
return {
|
||||
stamp: stamp,
|
||||
unknown1: unknown1,
|
||||
filesize: filesize,
|
||||
headsize: headsize,
|
||||
numSections: numSections,
|
||||
sectionOffsets: sectionOffsets
|
||||
}
|
||||
}
|
||||
|
||||
this.read3dInfo = function(view, offset, dataHandler) {
|
||||
var baseOff = offset;
|
||||
offset += 1; //skip dummy
|
||||
var numObjects = view.getUint8(offset++);
|
||||
var secSize = view.getUint16(offset, true);
|
||||
offset += 2;
|
||||
//unknown block. documentation out of 10
|
||||
var uhdSize = view.getUint16(offset, true);
|
||||
offset += 2;
|
||||
var usecSize = view.getUint16(offset, true);
|
||||
offset += 2;
|
||||
var unknown = view.getUint32(offset, true); //usually 0x0000017F
|
||||
offset += 4;
|
||||
var objectUnk = []
|
||||
for (var i=0; i<numObjects; i++) {
|
||||
var uk1 = view.getUint16(offset, true);
|
||||
var uk2 = view.getUint16(offset+2, true);
|
||||
objectUnk.push({uk1: uk1, uk2: uk2});
|
||||
offset += 4;
|
||||
}
|
||||
//info block
|
||||
var ihdSize = view.getUint16(offset, true);
|
||||
offset += 2;
|
||||
var isecSize = view.getUint16(offset, true);
|
||||
offset += 2;
|
||||
var objectData = []
|
||||
for (var i=0; i<numObjects; i++) {
|
||||
var data = dataHandler(view, offset, baseOff, i); //must return object with "nextoff" as offset after reading data
|
||||
objectData.push(data)
|
||||
offset = data.nextoff;
|
||||
}
|
||||
|
||||
var names = []
|
||||
for (var i=0; i<numObjects; i++) {
|
||||
var name = "";
|
||||
for (var j=0; j<16; j++) {
|
||||
name += readChar(view, offset++)
|
||||
}
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
return {
|
||||
numObjects: numObjects,
|
||||
unknown: unknown,
|
||||
objectUnk: objectUnk,
|
||||
objectData: objectData,
|
||||
names: names,
|
||||
nextoff: offset
|
||||
}
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
//
|
||||
// nkm.js
|
||||
//--------------------
|
||||
// Loads nkm files and provides a variety of functions for accessing and using the data.
|
||||
//
|
||||
// nkm files usually drive the game logic of tracks (checkpoints, ai waypoints, objects on track) so it's pretty
|
||||
// crucial to have this up and running.
|
||||
//
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.nkm = function(input, mkwii) {
|
||||
//todo, support versions for other games (MKWii etc)
|
||||
|
||||
this.load = load;
|
||||
var thisObj = this;
|
||||
var handlers = [];
|
||||
|
||||
window.onmousemove = function(evt) {
|
||||
mouseX = evt.pageX;
|
||||
mouseY = evt.pageY;
|
||||
}
|
||||
|
||||
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
||||
|
||||
function load(buffer, mkwii) {
|
||||
var view = new DataView(buffer);
|
||||
thisObj.stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
thisObj.version = view.getUint16(0x4, true);
|
||||
var n = view.getUint16(0x6, true);
|
||||
var off = 8;
|
||||
var sections = {};
|
||||
|
||||
for (var i=0; i<(n-8)/4; i++) {
|
||||
var soff = view.getUint32(off, true);
|
||||
var section = readSection(view, soff+n)
|
||||
sections[section.type] = section;
|
||||
|
||||
off += 4;
|
||||
}
|
||||
thisObj.sections = sections;
|
||||
}
|
||||
|
||||
function readSection(view, off) {
|
||||
var obj = {};
|
||||
obj.type = readChar(view, off+0x0)+readChar(view, off+0x1)+readChar(view, off+0x2)+readChar(view, off+0x3);
|
||||
|
||||
if (obj.type == "STAG") {
|
||||
handlers["STAG"](view, off, obj);
|
||||
} else {
|
||||
var handler = handlers[obj.type];
|
||||
if (handler == null) {
|
||||
console.error("Unknown NKM section type "+obj.type+"!");
|
||||
return obj;
|
||||
}
|
||||
var entN = view.getUint32(off+0x4, true);
|
||||
off += 0x8;
|
||||
var entries = [];
|
||||
for (var i=0; i<entN; i++) {
|
||||
var item = handler(view, off);
|
||||
entries.push(item);
|
||||
off = item.nextOff;
|
||||
}
|
||||
obj.entries = entries;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["OBJI"] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.pos = vec3.create();
|
||||
obj.pos[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos[2] = view.getInt32(off+8, true)/4096;
|
||||
|
||||
obj.angle = [];
|
||||
obj.angle[0] = view.getInt32(off+0xC, true)/4096;
|
||||
obj.angle[1] = view.getInt32(off+0x10, true)/4096;
|
||||
obj.angle[2] = view.getInt32(off+0x14, true)/4096;
|
||||
|
||||
obj.scale = vec3.create();
|
||||
obj.scale[0] = view.getInt32(off+0x18, true)/4096;
|
||||
obj.scale[1] = view.getInt32(off+0x1C, true)/4096;
|
||||
obj.scale[2] = view.getInt32(off+0x20, true)/4096;
|
||||
|
||||
obj.ID = view.getUint16(off+0x24, true);
|
||||
obj.routeID = view.getUint16(off+0x26, true);
|
||||
|
||||
obj.setting1 = view.getUint32(off+0x28, true);
|
||||
obj.setting2 = view.getUint32(off+0x2C, true);
|
||||
obj.setting3 = view.getUint32(off+0x30, true);
|
||||
obj.setting4 = view.getUint32(off+0x34, true);
|
||||
|
||||
obj.timeTrials = view.getUint32(off+0x38, true);
|
||||
|
||||
obj.nextOff = off+0x3C;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["PATH"] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.routeID = view.getUint8(off);
|
||||
obj.loop = view.getUint8(off+1);
|
||||
obj.numPts = view.getUint16(off+2, true);
|
||||
obj.nextOff = off+0x4;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["POIT"] = function(view, off) {
|
||||
var obj = {};
|
||||
|
||||
obj.pos = vec3.create();
|
||||
obj.pos[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos[2] = view.getInt32(off+8, true)/4096;
|
||||
|
||||
obj.pointInd = view.getUint16(off+0xC, true);
|
||||
obj.duration = view.getUint16(off+0xE, true);
|
||||
obj.unknown = view.getUint32(off+0x10, true);
|
||||
|
||||
obj.nextOff = off+0x14;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["STAG"] = function(view, off, obj) {
|
||||
obj.courseID = view.getUint16(off+0x4, true);
|
||||
obj.laps = view.getUint16(off+0x6, true); //doubles as battle mode duration
|
||||
obj.unknown = view.getUint8(off+0x8);
|
||||
obj.fogEnable = view.getUint8(off+0x9);
|
||||
obj.fogMode = view.getUint8(off+0xA);
|
||||
obj.fogSlope = view.getUint8(off+0xB);
|
||||
|
||||
//skip 8 bytes of unknown, probably more fog stuff. (disabled for now)
|
||||
|
||||
obj.fogDist = view.getInt32(off+0x14, true)/4096;
|
||||
obj.fogCol = readRGB(view, off+0x18);
|
||||
obj.fogAlpha = view.getUint16(off+0x1A, true);
|
||||
|
||||
obj.kclCol = [readRGB(view, off+0x1C), readRGB(view, off+0x1E), readRGB(view, off+0x20), readRGB(view, off+0x22)];
|
||||
|
||||
//unknown 8 bytes again
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["KTPS"] = function(view, off) { //start positions
|
||||
var obj = {};
|
||||
obj.pos = vec3.create();
|
||||
obj.pos[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos[2] = view.getInt32(off+8, true)/4096;
|
||||
|
||||
obj.angle = [];
|
||||
obj.angle[0] = view.getInt32(off+0xC, true)/4096;
|
||||
obj.angle[1] = view.getInt32(off+0x10, true)/4096;
|
||||
obj.angle[2] = view.getInt32(off+0x14, true)/4096;
|
||||
|
||||
obj.id1 = view.getInt16(off+0x18, true);
|
||||
obj.id2 = view.getInt16(off+0x1A, true);
|
||||
obj.nextOff = off+0x1C;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["KTP2"] = handlers["KTPS"]; //must pass this point for lap to count. ids irrelevant
|
||||
handlers["KTPJ"] = function(view, off){
|
||||
var obj = handlers["KTPS"](view, off);
|
||||
obj.respawnID = view.getInt32(off+0x1C, true);
|
||||
obj.nextOff += 0x4;
|
||||
return obj;
|
||||
}; //respawn points. id1 is cpu route, id2 is item route
|
||||
handlers["KTPC"] = handlers["KTPS"]; //cannon positions. id1 is cpu route, id2 is cannon id
|
||||
handlers["KTPM"] = handlers["KTPS"]; //mission kart position. must pass for mission to succeed.
|
||||
|
||||
handlers["CPOI"] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.x1 = view.getInt32(off, true)/4096;
|
||||
obj.z1 = view.getInt32(off+0x4, true)/4096;
|
||||
obj.x2 = view.getInt32(off+0x8, true)/4096;
|
||||
obj.z2 = view.getInt32(off+0xC, true)/4096;
|
||||
obj.sinus = view.getInt32(off+0x10, true)/4096;
|
||||
obj.cosinus = view.getInt32(off+0x14, true)/4096;
|
||||
obj.distance = view.getInt32(off+0x18, true)/4096;
|
||||
|
||||
obj.nextSection = view.getInt16(off+0x1C, true);
|
||||
obj.currentSection = view.getInt16(off+0x1E, true);
|
||||
|
||||
obj.keyPoint = view.getInt16(off+0x20, true);
|
||||
obj.respawn = view.getUint8(off+0x22);
|
||||
obj.unknown = view.getUint8(off+0x23);
|
||||
|
||||
obj.nextOff = off+0x24;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["CPAT"] = function(view, off) { //checkpoint path
|
||||
var obj = {};
|
||||
obj.startInd = view.getInt16(off, true);
|
||||
obj.pathLen = view.getInt16(off+0x2, true);
|
||||
|
||||
obj.dest = [view.getInt8(off+0x4)];
|
||||
var tmp = view.getInt8(off+0x5)
|
||||
if (tmp != -1) obj.dest.push(tmp);
|
||||
|
||||
var tmp2 = view.getInt8(off+0x6)
|
||||
if (tmp2 != -1) obj.dest.push(tmp2);
|
||||
|
||||
obj.source = [view.getInt8(off+0x7)];
|
||||
|
||||
var tmp3 = view.getInt8(off+0x8)
|
||||
if (tmp3 != -1) obj.source.push(tmp3);
|
||||
|
||||
var tmp4 = view.getInt8(off+0x9)
|
||||
if (tmp4 != -1) obj.source.push(tmp4);
|
||||
|
||||
obj.sectionOrder = view.getInt16(off+0xA, true);
|
||||
|
||||
obj.nextOff = off+0xC;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["MEPA"] = function(view, off) { //checkpoint path
|
||||
var obj = {};
|
||||
obj.startInd = view.getInt16(off, true);
|
||||
obj.pathLen = view.getInt16(off+0x2, true);
|
||||
|
||||
obj.dest = [];
|
||||
var o = off+4;
|
||||
for(var i=0; i<8; i++) {
|
||||
var tmp = view.getInt8(o++);
|
||||
if (tmp != -1) obj.dest.push(tmp);
|
||||
}
|
||||
|
||||
obj.source = [];
|
||||
for(var i=0; i<8; i++) {
|
||||
var tmp = view.getInt8(o++);
|
||||
if (tmp != -1) obj.source.push(tmp);
|
||||
}
|
||||
|
||||
obj.nextOff = o;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["IPAT"] = handlers["CPAT"]; //item path
|
||||
handlers["EPAT"] = handlers["CPAT"]; //enemy path
|
||||
|
||||
handlers["IPOI"] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.pos = vec3.create();
|
||||
obj.pos[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos[2] = view.getInt32(off+8, true)/4096;
|
||||
obj.unknown1 = view.getUint8(off+0xC); //tends to be 0 or FF
|
||||
obj.unknown2 = view.getUint8(off+0xD);
|
||||
obj.unknown3 = view.getUint8(off+0xE);
|
||||
obj.unknown4 = view.getUint8(off+0xF);
|
||||
obj.unknown5 = view.getUint32(off+0x10, true); //tends to be 0
|
||||
|
||||
obj.nextOff = off+0x14;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["EPOI"] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.pos = vec3.create();
|
||||
obj.pos[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos[2] = view.getInt32(off+8, true)/4096;
|
||||
|
||||
obj.pointSize = view.getInt32(off+0xC, true)/4096;
|
||||
obj.cpuDrift = view.getUint16(off+0x10, true); //will find out what this means in due time, a watcher on this value while a cpu is going around the track should clear things up.
|
||||
obj.unknown1 = view.getUint16(off+0x12, true); //tends to be 0
|
||||
obj.unknown2 = view.getUint32(off+0x14, true); //tends to be 0
|
||||
|
||||
obj.nextOff = off+0x18;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["MEPO"] = function(view, off) { //theres usually 5 of these LOL!!! im not sorry
|
||||
var obj = {};
|
||||
obj.pos = vec3.create();
|
||||
obj.pos[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos[2] = view.getInt32(off+8, true)/4096;
|
||||
|
||||
obj.pointSize = view.getInt32(off+0xC, true)/4096;
|
||||
obj.cpuDrift = view.getUint16(off+0x10, true); //will find out what this means in due time, a watcher on this value while a cpu is going around the track should clear things up.
|
||||
obj.unknown1 = view.getUint16(off+0x12, true); //tends to be 0
|
||||
obj.unknown1 = view.getUint32(off+0x14, true); //tends to be 0
|
||||
|
||||
obj.nextOff = off+0x18;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["AREA"] = function(view, off) { //area for cameras. this section is ridiculous - will need thorough investigation if we want to get race spectate cameras working.
|
||||
var obj = {};
|
||||
obj.pos = vec3.create();
|
||||
obj.pos[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos[2] = view.getInt32(off+8, true)/4096;
|
||||
|
||||
obj.dimensions = vec3.create();
|
||||
obj.dimensions[0] = view.getInt32(off+0xC, true)/4096;
|
||||
obj.dimensions[1] = view.getInt32(off+0x10, true)/4096;
|
||||
obj.dimensions[2] = view.getInt32(off+0x14, true)/4096;
|
||||
|
||||
//44 bytes of unknown, ouch!
|
||||
|
||||
obj.came = view.getUint8(off+0x43);
|
||||
|
||||
obj.one = view.getUint32(off+0x44, true); //good ole one
|
||||
|
||||
obj.nextOff = off+0x48;
|
||||
return obj;
|
||||
}
|
||||
|
||||
handlers["CAME"] = function(view, off) { //cameras. not really much known about these right now.
|
||||
var obj = {};
|
||||
obj.pos1 = vec3.create();
|
||||
obj.pos1[0] = view.getInt32(off, true)/4096;
|
||||
obj.pos1[1] = view.getInt32(off+4, true)/4096;
|
||||
obj.pos1[2] = view.getInt32(off+8, true)/4096;
|
||||
|
||||
obj.angle = vec3.create();
|
||||
obj.angle[0] = view.getInt32(off+0xC, true)/4096;
|
||||
obj.angle[1] = view.getInt32(off+0x10, true)/4096;
|
||||
obj.angle[2] = view.getInt32(off+0x14, true)/4096;
|
||||
|
||||
obj.pos2 = vec3.create();
|
||||
obj.pos2[0] = view.getInt32(off+0x18, true)/4096;
|
||||
obj.pos2[1] = view.getInt32(off+0x1C, true)/4096;
|
||||
obj.pos2[2] = view.getInt32(off+0x20, true)/4096;
|
||||
|
||||
obj.pos3 = vec3.create();
|
||||
obj.pos3[0] = view.getInt32(off+0x24, true)/4096;
|
||||
obj.pos3[1] = view.getInt32(off+0x28, true)/4096;
|
||||
obj.pos3[2] = view.getInt32(off+0x2C, true)/4096;
|
||||
|
||||
//44 bytes of unknown, ouch!
|
||||
|
||||
obj.zoomSpeedM1 = view.getInt16(off+0x30, true)/4096;
|
||||
|
||||
obj.zoomStart = view.getInt16(off+0x32, true)/4096; //alters zoom somehow
|
||||
obj.zoomEnd = view.getInt16(off+0x34, true)/4096;
|
||||
|
||||
obj.zoomSpeedM2 = view.getInt16(off+0x36, true)/4096;
|
||||
obj.zoomMark1 = view.getInt16(off+0x38, true)/4096; //zoom speed changes at zoom marks
|
||||
obj.zoomMark2 = view.getInt16(off+0x3A, true)/4096;
|
||||
|
||||
obj.zoomSpeed = view.getInt16(off+0x3C, true)/4096;
|
||||
obj.camType = view.getInt16(off+0x3E, true);
|
||||
obj.camRoute = view.getInt16(off+0x40, true);
|
||||
obj.routeSpeed = view.getInt16(off+0x42, true);
|
||||
obj.pointSpeed = view.getInt16(off+0x44, true);
|
||||
obj.duration = view.getInt16(off+0x46, true);
|
||||
obj.nextCam = view.getInt16(off+0x48, true);
|
||||
|
||||
obj.firstCam = view.getUint8(off+0x4A);
|
||||
obj.one = view.getUint8(off+0x4B); //tends to be 1 if cam type is 5
|
||||
|
||||
obj.nextOff = off+0x4C;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
function readRGB(view, offset) {
|
||||
var dat = view.getUint16(offset, true);
|
||||
var col = [dat&31, (dat>>5)&31, (dat>>10)&31];
|
||||
return col;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
if (typeof input == "string") {
|
||||
var xml = new XMLHttpRequest();
|
||||
xml.responseType = "arraybuffer";
|
||||
xml.open("GET", input, true);
|
||||
xml.onload = function() {
|
||||
load(xml.response);
|
||||
}
|
||||
xml.send();
|
||||
} else {
|
||||
load(input, mkwii);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
//
|
||||
// nsbca.js
|
||||
//--------------------
|
||||
// Reads NSBCA files (bone animations) for use in combination with an NSBMD (model) file
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /formats/nitro.js
|
||||
//
|
||||
|
||||
// most investigation done by florian for the mkds course modifier.
|
||||
// I've tried to keep things much simpler than they were in his code.
|
||||
|
||||
window.nsbca = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var animData;
|
||||
var speeds = [1.0, 0.5, 1/3];
|
||||
var mainObj = this;
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
var tex;
|
||||
|
||||
//nitro 3d header
|
||||
header = nitro.readHeader(view);
|
||||
if (header.stamp != "BCA0") throw "NSBCA invalid. Expected BCA0, found "+header.stamp;
|
||||
if (header.numSections > 1) throw "NSBCA invalid. Too many sections - should have 1 maximum.";
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
var stamp = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (stamp != "JNT0") throw "NSBCA invalid. Expected JNT0, found "+stamp;
|
||||
|
||||
animData = nitro.read3dInfo(view, mainOff+8, animInfoHandler);
|
||||
mainObj.animData = animData;
|
||||
}
|
||||
|
||||
function animInfoHandler(view, off, base) {
|
||||
var offset = mainOff + view.getUint32(off, true);
|
||||
var obj = {nextoff: off + 4}
|
||||
readAnim(view, offset, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
function readAnim(view, off, obj) {
|
||||
obj.baseOff = off;
|
||||
obj.stamp = readChar(view, off+0x0)+readChar(view, off+0x2)+readChar(view, off+0x3);
|
||||
obj.frames = view.getUint16(off+0x4, true);
|
||||
obj.numObj = view.getUint16(off+0x6, true);
|
||||
obj.unknown = view.getUint32(off+0x8, true); //NOTE: this may be a flag. used later to specify extra frames if not = 3
|
||||
obj.off1 = view.getUint32(off+0xC, true);
|
||||
obj.off2 = view.getUint32(off+0x10, true); //offset to rotation data
|
||||
off += 0x14;
|
||||
var transforms = [];
|
||||
for (var i=0; i<obj.numObj; i++) {
|
||||
var off2 = view.getUint16(off, true)+obj.baseOff;
|
||||
transforms.push(readTrans(view, off2, obj));
|
||||
off += 2;
|
||||
}
|
||||
obj.trans = transforms;
|
||||
}
|
||||
|
||||
function readTrans(view, off, obj) {
|
||||
var flag = view.getUint16(off, true); //--zyx-Sr-RZYX-T-
|
||||
|
||||
off += 4;
|
||||
|
||||
var transform = {};
|
||||
|
||||
if (!((flag>>1) & 1)) { //T: translation
|
||||
var translate = [[], [], []]; //store translations in x,y,z arrays
|
||||
var tlExtra = [];
|
||||
|
||||
for (var i=0; i<3; i++) { //iterate over x y z (for translation)
|
||||
var f = (flag>>(3+i))&1;
|
||||
if (f) { //one value
|
||||
translate[i].push(view.getInt32(off, true)/4096);
|
||||
off += 4;
|
||||
} else { //credit to florian for cracking this.
|
||||
var inf = {};
|
||||
inf.startFrame = view.getUint16(off, true)
|
||||
var dat = view.getUint16(off+2, true)
|
||||
inf.endFrame = dat&0x0FFF;
|
||||
inf.halfSize = ((dat>>12)&3);
|
||||
inf.speed = speeds[((dat>>14)&3)];
|
||||
inf.off = view.getUint32(off+4, true);
|
||||
|
||||
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
||||
var length = Math.floor((obj.frames+extra)*inf.speed);
|
||||
var w = (inf.halfSize)?2:4;
|
||||
|
||||
var off2 = obj.baseOff+inf.off;
|
||||
for (var j=0; j<length; j++) {
|
||||
translate[i].push(((inf.halfSize)?view.getInt16(off2, true):view.getInt32(off2, true))/4096);
|
||||
off2 += w;
|
||||
}
|
||||
tlExtra[i] = inf;
|
||||
off += 8;
|
||||
}
|
||||
}
|
||||
|
||||
transform.translate = translate;
|
||||
transform.tlExtra = tlExtra;
|
||||
}
|
||||
|
||||
if (!((flag>>6) & 1)) { //R: rotation, which is both fun and exciting.
|
||||
|
||||
var rotate = [];
|
||||
var rotExtra;
|
||||
|
||||
var f = (flag>>8)&1;
|
||||
if (f) { //one value
|
||||
rotate.push(readRotation(view, off, obj));
|
||||
off += 4;
|
||||
} else { //credit to florian for cracking this.
|
||||
var inf = {};
|
||||
inf.startFrame = view.getUint16(off, true)
|
||||
var dat = view.getUint16(off+2, true) //low 12 bits are end frame, high 4 are size flag and speed
|
||||
inf.endFrame = dat&0x0FFF;
|
||||
inf.halfSize = ((dat>>12)&3); //not used by rotation?
|
||||
inf.speed = speeds[((dat>>14)&3)];
|
||||
inf.off = view.getUint32(off+4, true);
|
||||
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
||||
//florian's rotate code seems to ignore this extra value. I'll need more examples of nsbca to test this more thoroughly.
|
||||
var length = Math.floor((obj.frames+extra)*inf.speed);
|
||||
|
||||
var off2 = obj.baseOff+inf.off;
|
||||
try {
|
||||
for (var j=0; j<length; j++) {
|
||||
rotate.push(readRotation(view, off2, obj));
|
||||
off2 += 2;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
rotExtra = inf;
|
||||
off += 8;
|
||||
}
|
||||
|
||||
transform.rotate = rotate;
|
||||
transform.rotExtra = rotExtra;
|
||||
}
|
||||
|
||||
if (!((flag>>9) & 1)) { //S: scale
|
||||
var scale = [[], [], []]; //store scales in x,y,z arrays
|
||||
var scExtra = [];
|
||||
|
||||
for (var i=0; i<3; i++) { //iterate over x y z (for scale)
|
||||
var f = (flag>>(11+i))&1;
|
||||
if (f) { //one value
|
||||
scale[i].push({
|
||||
s1: view.getInt32(off, true)/4096,
|
||||
s2: view.getInt32(off, true)/4096
|
||||
});
|
||||
off += 8;
|
||||
} else { //credit to florian for cracking this.
|
||||
var inf = {};
|
||||
inf.startFrame = view.getUint16(off, true)
|
||||
var dat = view.getUint16(off+2, true)
|
||||
inf.endFrame = dat&0x0FFF;
|
||||
inf.halfSize = ((dat>>12)&3);
|
||||
inf.speed = speeds[((dat>>14)&3)];
|
||||
inf.off = view.getUint32(off+4, true);
|
||||
|
||||
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
||||
var length = Math.ceil((obj.frames+extra)*inf.speed);
|
||||
var w = ((inf.halfSize)?2:4);
|
||||
|
||||
var off2 = obj.baseOff+inf.off;
|
||||
for (var j=0; j<length; j++) {
|
||||
scale[i].push({
|
||||
s1: ((inf.halfSize)?view.getInt16(off2, true):view.getInt32(off2, true))/4096,
|
||||
s2: ((inf.halfSize)?view.getInt16(off2+w, true):view.getInt32(off2+w, true))/4096
|
||||
});
|
||||
off2 += w*2;
|
||||
}
|
||||
scExtra[i] = inf;
|
||||
off += 8;
|
||||
}
|
||||
}
|
||||
|
||||
transform.scale = scale;
|
||||
transform.scExtra = scExtra;
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
function readRotation(view, off, obj) {
|
||||
var dat = view.getInt16(off, true);
|
||||
var ind = (dat&0x7FFF);
|
||||
var mode = (dat>>15);
|
||||
|
||||
if (mode) { //rotation is pivot
|
||||
var off2 = obj.baseOff+obj.off1+ind*6; //jump to rotation data
|
||||
return {
|
||||
pivot: true,
|
||||
param: view.getUint16(off2, true),
|
||||
a: view.getInt16(off2+2, true)/4096,
|
||||
b: view.getInt16(off2+4, true)/4096
|
||||
};
|
||||
} else {
|
||||
var off2 = obj.baseOff+obj.off2+ind*10; //jump to rotation data
|
||||
var d1 = view.getInt16(off2, true);
|
||||
var d2 = view.getInt16(off2+2, true);
|
||||
var d3 = view.getInt16(off2+4, true);
|
||||
var d4 = view.getInt16(off2+6, true);
|
||||
var d5 = view.getInt16(off2+8, true);
|
||||
|
||||
var i6 = ((d5&7)<<12) | ((d1&7)<<9) | ((d2&7)<<6) | ((d3&7)<<3) | ((d4&7));
|
||||
if (i6&4096) i6 = (-8192)+i6;
|
||||
|
||||
var v1 = [d1>>3, d2>>3, d3>>3]
|
||||
var v2 = [d4>>3, d5>>3, i6]
|
||||
|
||||
vec3.scale(v1, v1, 1/4096);
|
||||
vec3.scale(v2, v2, 1/4096);
|
||||
var v3 = vec3.cross([], v1, v2)
|
||||
|
||||
var mat = [
|
||||
v1[0], v1[1], v1[2],
|
||||
v2[0], v2[1], v2[2],
|
||||
v3[0], v3[1], v3[2]
|
||||
]
|
||||
|
||||
return {
|
||||
pivot: false,
|
||||
mat: mat
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
//
|
||||
// nsbmd.js
|
||||
//--------------------
|
||||
// Reads NSBMD models and any texture data within them.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /formats/nitro.js
|
||||
// /formats/nsbtx.js
|
||||
//
|
||||
|
||||
window.nsbmd = function(input) {
|
||||
|
||||
var mainOff, modelData, texPalOff, materials;
|
||||
var mainObj = this;
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
|
||||
function load(input) {
|
||||
mainObj.hasBillboards = false;
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
var tex;
|
||||
|
||||
//nitro 3d header
|
||||
header = nitro.readHeader(view);
|
||||
if (header.stamp != "BMD0") throw "NSBMD invalid. Expected BMD0, found "+header.stamp;
|
||||
if (header.numSections > 2) throw "NSBMD invalid. Too many sections - should have 2 maximum.";
|
||||
if (header.numSections == 2) tex = new nsbtx(input.slice(header.sectionOffsets[1]), true, true);
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
var stamp = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (stamp != "MDL0") throw "NSBMD invalid. Expected MDL0, found "+stamp;
|
||||
|
||||
mainObj.tex = tex;
|
||||
|
||||
modelData = nitro.read3dInfo(view, mainOff+8, modelInfoHandler);
|
||||
mainObj.modelData = modelData;
|
||||
}
|
||||
|
||||
function modelInfoHandler(view, offset) {
|
||||
var mdlOff = view.getUint32(offset, true);
|
||||
|
||||
var off = mainOff+mdlOff;
|
||||
var obj = readModelData(view, off);
|
||||
obj.nextoff = offset+4;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function readModelData(view, offset) {
|
||||
var head = {}
|
||||
head.blockSize = view.getUint32(offset, true);
|
||||
head.bonesOffset = offset+view.getUint32(offset+4, true);
|
||||
head.materialsOffset = offset+view.getUint32(offset+8, true);
|
||||
head.polyStartOffset = offset+view.getUint32(offset+0xC, true);
|
||||
head.polyEndOffset = offset+view.getUint32(offset+0x10, true);
|
||||
|
||||
head.numObjects = view.getUint8(offset+0x17);
|
||||
head.numMaterials = view.getUint8(offset+0x18);
|
||||
head.numPolys = view.getUint8(offset+0x19);
|
||||
head.maxStack = view.getUint8(offset+0x1A);
|
||||
|
||||
head.scale = view.getInt32(offset+0x1C, true)/4096;
|
||||
|
||||
head.numVerts = view.getUint16(offset+0x24, true);
|
||||
head.numSurfaces = view.getUint16(offset+0x26, true);
|
||||
head.numTriangles = view.getUint16(offset+0x28, true);
|
||||
head.numQuads = view.getUint16(offset+0x2A, true);
|
||||
|
||||
head.bboxX = view.getInt16(offset+0x2C, true)/4096;
|
||||
head.bboxY = view.getInt16(offset+0x2E, true)/4096;
|
||||
head.bboxZ = view.getInt16(offset+0x30, true)/4096;
|
||||
head.bboxWidth = view.getInt16(offset+0x32, true)/4096;
|
||||
head.bboxHeight = view.getInt16(offset+0x34, true)/4096;
|
||||
head.bboxDepth = view.getInt16(offset+0x36, true)/4096;
|
||||
//head.runtimeData = view.getUint64(offset+0x38, true);
|
||||
texPalOff = head.materialsOffset; //leak into local scope so it can be used by tex and pal bindings
|
||||
|
||||
var objects = nitro.read3dInfo(view, offset+0x40, objInfoHandler);
|
||||
var polys = nitro.read3dInfo(view, head.polyStartOffset, polyInfoHandler);
|
||||
|
||||
materials = nitro.read3dInfo(view, head.materialsOffset+4, matInfoHandler);
|
||||
|
||||
var tex = nitro.read3dInfo(view, head.materialsOffset+view.getUint16(head.materialsOffset, true), texInfoHandler);
|
||||
var palt = nitro.read3dInfo(view, head.materialsOffset+view.getUint16(head.materialsOffset+2, true), palInfoHandler);
|
||||
|
||||
var commands = parseBones(head.bonesOffset, view, polys, materials, objects, head.maxStack);
|
||||
|
||||
return {head: head, objects: objects, polys: polys, materials: materials, tex:tex, palt:palt, commands:commands}
|
||||
}
|
||||
|
||||
function parseBones(offset, view, polys, materials, objects, maxStack) {
|
||||
var last;
|
||||
var commands = [];
|
||||
|
||||
var freeStack = maxStack;
|
||||
var forceID=null;
|
||||
var lastMat = null;
|
||||
|
||||
while (offset<texPalOff) { //bones
|
||||
last = view.getUint8(offset++);
|
||||
console.log("bone cmd: 0x"+last.toString(16));
|
||||
switch (last) {
|
||||
case 0x06: //bind object transforms to parent. bone exists but is not placed in the stack
|
||||
var obj = view.getUint8(offset++);
|
||||
var parent = view.getUint8(offset++);
|
||||
var zero = view.getUint8(offset++);
|
||||
|
||||
var object = objects.objectData[obj];
|
||||
object.parent = parent;
|
||||
|
||||
commands.push({obj:obj, parent:parent, stackID:freeStack++});
|
||||
break;
|
||||
case 0x26:
|
||||
case 0x46: //placed in the stack at stack id
|
||||
var obj = view.getUint8(offset++);
|
||||
var parent = view.getUint8(offset++);
|
||||
var zero = view.getUint8(offset++);
|
||||
var stackID = view.getUint8(offset++);
|
||||
|
||||
var object = objects.objectData[obj];
|
||||
object.parent = parent;
|
||||
|
||||
commands.push({obj:obj, parent:parent, stackID:((last == 0x26)?stackID:freeStack++), restoreID:((last == 0x46)?stackID:null)});
|
||||
break;
|
||||
case 0x66: //has ability to restore to another stack id. no idea how this works
|
||||
var obj = view.getUint8(offset++);
|
||||
var parent = view.getUint8(offset++);
|
||||
var zero = view.getUint8(offset++);
|
||||
var stackID = view.getUint8(offset++);
|
||||
var restoreID = view.getUint8(offset++);
|
||||
|
||||
var object = objects.objectData[obj];
|
||||
object.parent = parent;
|
||||
|
||||
commands.push({obj:obj, parent:parent, stackID:stackID, restoreID:restoreID});
|
||||
break;
|
||||
case 0x04:
|
||||
case 0x24:
|
||||
case 0x44: //bind material to polygon: matID, 5, polyID
|
||||
var mat = view.getUint8(offset++);
|
||||
lastMat = mat;
|
||||
var five = view.getUint8(offset++); //???
|
||||
var poly = view.getUint8(offset++);
|
||||
polys.objectData[poly].stackID = (forceID == null)?(commands[commands.length-1].stackID):forceID;
|
||||
polys.objectData[poly].mat = mat;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2: //node visibility (maybe to implement this set matrix to 0)
|
||||
|
||||
var node = view.getUint8(offset++);
|
||||
var vis = view.getUint8(offset++);
|
||||
objects.objectData[node].vis = vis;
|
||||
console.log("visibility thing "+node);
|
||||
if (node > 10) debugger;
|
||||
break;
|
||||
case 3: //stack id for poly (wit)
|
||||
forceID = view.getUint8(offset++);
|
||||
console.log("stackid is "+forceID);
|
||||
case 0:
|
||||
break;
|
||||
case 5:
|
||||
//i don't... what??
|
||||
//holy shp!
|
||||
var poly = view.getUint8(offset++);
|
||||
polys.objectData[poly].stackID = (stackID == null)?(commands[commands.length-1].forceID):forceID;
|
||||
polys.objectData[poly].mat = lastMat;
|
||||
|
||||
break;
|
||||
case 7:
|
||||
//sets object to be billboard
|
||||
var obj = view.getUint8(offset++);
|
||||
objects.objectData[obj].billboardMode = 1;
|
||||
mainObj.hasBillboards = true;
|
||||
break;
|
||||
case 8:
|
||||
//sets object to be billboard around only y axis. (xz remain unchanged)
|
||||
var obj = view.getUint8(offset++);
|
||||
objects.objectData[obj].billboardMode = 2;
|
||||
mainObj.hasBillboards = true;
|
||||
break;
|
||||
case 0x0b:
|
||||
break; //begin, not quite sure what of. doesn't seem to change anything
|
||||
case 0x2b:
|
||||
break; //end
|
||||
default:
|
||||
console.log("bone transform unknown: "+last);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if (window.throwWhatever) debugger;
|
||||
return commands;
|
||||
}
|
||||
|
||||
function matInfoHandler(view, off, base) {
|
||||
var offset = texPalOff + view.getUint32(off, true);
|
||||
|
||||
var rel = 0;
|
||||
/*while (rel < 40) {
|
||||
var flags = view.getUint16(offset+rel, true);
|
||||
if ((flags&15)==15) console.log("rel at "+rel);
|
||||
rel += 2;
|
||||
}*/
|
||||
|
||||
var polyAttrib = view.getUint16(offset+12, true);
|
||||
console.log(polyAttrib);
|
||||
|
||||
var flags = view.getUint16(offset+22, true); //other info in here is specular data etc.
|
||||
|
||||
//scale starts at 44;
|
||||
|
||||
var mat;
|
||||
offset += 44;
|
||||
switch ((flags>>14) & 0x03) { //texture scaling mode
|
||||
case 0:
|
||||
mat = mat3.create(); //no scale
|
||||
break;
|
||||
case 1:
|
||||
mat = mat3.create();
|
||||
mat3.scale(mat, mat, [view.getInt32(offset, true)/4096, view.getInt32(offset+4, true)/4096]);
|
||||
//mat3.translate(mat, mat, [-anim.translateS[(texFrame>>anim.frameStep.translateS)%anim.translateS.length], anim.translateT[(texFrame>>anim.frameStep.translateT)%anim.translateT.length]]) //for some mystery reason I need to negate the S translation
|
||||
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
mat = mat3.create(); //custom tex mat
|
||||
alert("custom");
|
||||
for (var i=0; i<16; i++) {
|
||||
mat[i] = view.getInt32(offset, true)/4096;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
var cullMode = ((polyAttrib>>6)&3);
|
||||
|
||||
var alpha = ((polyAttrib>>16)&31)/31;
|
||||
if (alpha == 0) alpha = 1;
|
||||
|
||||
return {
|
||||
height: 8 << ((flags>>7)&7),
|
||||
width: 8 << ((flags>>4)&7),
|
||||
repeatX: flags&1,
|
||||
repeatY: (flags>>1)&1,
|
||||
flipX: (flags>>2)&1,
|
||||
flipY: (flags>>3)&1,
|
||||
texMat: mat,
|
||||
alpha: alpha,
|
||||
cullMode: cullMode,
|
||||
nextoff: off + 4
|
||||
}
|
||||
}
|
||||
|
||||
function texInfoHandler(view, off, base, ind) {
|
||||
var oDat = texPalOff+view.getUint16(off, true); //contains offset to array of materials to bind to
|
||||
var num = view.getUint8(off+2, true);
|
||||
var mats = [];
|
||||
for (var i=0; i<num; i++) {
|
||||
var mat = view.getUint8(oDat++);
|
||||
materials.objectData[mat].tex = ind; //bind to this material
|
||||
mats.push(mat);
|
||||
}
|
||||
return {
|
||||
mats: mats,
|
||||
nextoff: off + 4
|
||||
}
|
||||
}
|
||||
|
||||
function palInfoHandler(view, off, base, ind) {
|
||||
var oDat = texPalOff+view.getUint16(off, true); //contains offset to array of materials to bind to
|
||||
var num = view.getUint8(off+2, true);
|
||||
var mats = [];
|
||||
for (var i=0; i<num; i++) {
|
||||
var mat = view.getUint8(oDat++);
|
||||
materials.objectData[mat].pal = ind; //bind to this material
|
||||
mats.push(mat);
|
||||
}
|
||||
return {
|
||||
mats: mats,
|
||||
nextoff: off + 4
|
||||
}
|
||||
}
|
||||
|
||||
function polyInfoHandler(view, off, base) {
|
||||
var offset = base + view.getUint32(off, true);
|
||||
var dlStart = offset+view.getUint32(offset+8, true);
|
||||
var displayList = view.buffer.slice(dlStart, dlStart+view.getUint32(offset+0xC, true))
|
||||
return {
|
||||
nextoff: off + 4,
|
||||
disp: displayList
|
||||
}
|
||||
}
|
||||
|
||||
function objInfoHandler(view, off, base) {
|
||||
var offset = base + view.getUint32(off, true);
|
||||
|
||||
var flag = view.getUint16(offset, true); //flag format nnnn psrt
|
||||
var rotTerm1 = view.getInt16(offset+0x2, true)/4096; //first term of rotate mat if present
|
||||
var translate = vec3.create();
|
||||
if (!(flag&1)) { //translate (t) flag is 0
|
||||
translate[0] = view.getInt32(offset+0x4, true)/4096;
|
||||
translate[1] = view.getInt32(offset+0x8, true)/4096;
|
||||
translate[2] = view.getInt32(offset+0xC, true)/4096;
|
||||
offset += 0xC;
|
||||
}
|
||||
var pivot;
|
||||
var A, B, neg, mode;
|
||||
if (flag&8) { //pivot exists
|
||||
pivot = new Float32Array([0,0,0,0,0,0,0,0,0]);
|
||||
mode = (flag>>4)&15;
|
||||
neg = (flag>>8)&15;
|
||||
A = view.getInt16(offset+0x4, true)/4096;
|
||||
B = view.getInt16(offset+0x6, true)/4096;
|
||||
|
||||
pivot[mode] = (neg&1)?-1:1;
|
||||
var horiz = mode%3;
|
||||
var vert = Math.floor(mode/3)
|
||||
var left = (horiz==0)?1:0; var top = ((vert==0)?1:0)*3;
|
||||
var right = (horiz==2)?1:2; var btm = ((vert==2)?1:2)*3;
|
||||
pivot[left+top] = A;
|
||||
pivot[right+top] = B;
|
||||
pivot[left+btm] = (neg&2)?-B:B;
|
||||
pivot[right+btm] = (neg&4)?-A:A;
|
||||
|
||||
offset += 4;
|
||||
} else {
|
||||
pivot = mat3.create()
|
||||
}
|
||||
var scale = vec3.create();
|
||||
if (!(flag&4)) {
|
||||
scale[0] = view.getInt32(offset+0x4, true)/4096;
|
||||
scale[1] = view.getInt32(offset+0x8, true)/4096;
|
||||
scale[2] = view.getInt32(offset+0xC, true)/4096;
|
||||
offset += 0xC;
|
||||
} else {
|
||||
scale[0] = 1;
|
||||
scale[1] = 1;
|
||||
scale[2] = 1;
|
||||
}
|
||||
if ((!(flag&8)) && (!(flag&2))) { //rotate matrix, replaces pivot
|
||||
pivot[0] = rotTerm1;
|
||||
pivot[1] = view.getInt16(offset+0x4, true)/4096;
|
||||
pivot[2] = view.getInt16(offset+0x6, true)/4096;
|
||||
pivot[3] = view.getInt16(offset+0x8, true)/4096;
|
||||
pivot[4] = view.getInt16(offset+0xA, true)/4096;
|
||||
pivot[5] = view.getInt16(offset+0xC, true)/4096;
|
||||
pivot[6] = view.getInt16(offset+0xE, true)/4096;
|
||||
pivot[7] = view.getInt16(offset+0x10, true)/4096;
|
||||
pivot[8] = view.getInt16(offset+0x12, true)/4096;
|
||||
offset += 16;
|
||||
}
|
||||
var mat = mat4.create();
|
||||
mat4.translate(mat, mat, translate);
|
||||
mat4.multiply(mat, mat, mat4FromMat3(pivot));
|
||||
mat4.scale(mat, mat, scale);
|
||||
return {
|
||||
translate: translate,
|
||||
pivot: pivot,
|
||||
|
||||
pA: A,
|
||||
pB: B,
|
||||
pMode: mode,
|
||||
pNeg: neg,
|
||||
|
||||
scale: scale,
|
||||
flag: flag,
|
||||
mat: mat,
|
||||
billboardMode: 0,
|
||||
nextoff: off + 4
|
||||
}
|
||||
}
|
||||
|
||||
function mat4FromMat3(mat) {
|
||||
dest = mat4.create();
|
||||
|
||||
dest[0] = mat[0];
|
||||
dest[1] = mat[1];
|
||||
dest[2] = mat[2];
|
||||
dest[3] = 0;
|
||||
|
||||
dest[4] = mat[3];
|
||||
dest[5] = mat[4];
|
||||
dest[6] = mat[5];
|
||||
dest[7] = 0;
|
||||
|
||||
dest[8] = mat[6];
|
||||
dest[9] = mat[7];
|
||||
dest[10] = mat[8];
|
||||
dest[11] = 0;
|
||||
|
||||
dest[12] = 0;
|
||||
dest[13] = 0;
|
||||
dest[14] = 0;
|
||||
dest[15] = 1;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
//
|
||||
// nsbta.js
|
||||
//--------------------
|
||||
// Reads NSBTA files (texture uv animation via uv transform matrices within a polygon) for use in combination with an NSBMD (model) file
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /formats/nitro.js
|
||||
//
|
||||
|
||||
// man oh man if only there were some documentation on this that weren't shoddily written code in mkds course modifier
|
||||
// well i guess we can find out how the format works
|
||||
// together :')
|
||||
|
||||
window.nsbta = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var animData;
|
||||
var mainObj = this;
|
||||
var prop = [
|
||||
"scaleS",
|
||||
"scaleT",
|
||||
"rotation",
|
||||
"translateS",
|
||||
"translateT"
|
||||
]
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
var tex;
|
||||
|
||||
//nitro 3d header
|
||||
header = nitro.readHeader(view);
|
||||
if (header.stamp != "BTA0") throw "NSBTA invalid. Expected BTA0, found "+header.stamp;
|
||||
if (header.numSections > 1) throw "NSBTA invalid. Too many sections - should have 1 maximum.";
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
var stamp = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (stamp != "SRT0") throw "NSBTA invalid. Expected SRT0, found "+stamp;
|
||||
|
||||
animData = nitro.read3dInfo(view, mainOff+8, animInfoHandler);
|
||||
mainObj.animData = animData;
|
||||
}
|
||||
|
||||
function animInfoHandler(view, offset) {
|
||||
var animOff = view.getUint32(offset, true);
|
||||
|
||||
var off = mainOff+animOff;
|
||||
var obj = readAnimData(view, off);
|
||||
obj.nextoff = offset+4;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function readAnimData(view, offset) {
|
||||
var stamp = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3); //should be M_AT, where _ is a 0 character
|
||||
var unknown1 = view.getUint16(offset+4, true);
|
||||
var unknown2 = view.getUint8(offset+6, false);
|
||||
var unknown3 = view.getUint8(offset+7, false);
|
||||
var data = nitro.read3dInfo(view, offset+8, matInfoHandler);
|
||||
return {data: data, nextoff: data.nextoff};
|
||||
}
|
||||
|
||||
function matInfoHandler(view, offset, base) {
|
||||
// there doesn't seem to be any documentation on this so I'm going to take the first step and maybe explain a few things here:
|
||||
// each material has 5 sets of 16 bit values of the following type:
|
||||
//
|
||||
// frames: determines the number of frames worth of transforms of this type are stored
|
||||
// flags: if >4096 then multiple frames are used instead of inline data. not much else is known
|
||||
// offset/data: depending on previous flag, either points to an array of data or provides the data for the sole frame. relative to base of this 3dinfoobject
|
||||
// data2: used for rotation matrix (second value)
|
||||
//
|
||||
// order is as follows:
|
||||
// scaleS, scaleT, rotation, translateS, translateT (all values are signed fixed point 1.3.12)
|
||||
//
|
||||
// note: rotation external data has two 16 bit integers instead of one per frame.
|
||||
//
|
||||
// also!! rotation matrices work as follows:
|
||||
//
|
||||
// | B A |
|
||||
// | -A B |
|
||||
//
|
||||
// kind of like nsbmd pivot
|
||||
|
||||
var obj = {}
|
||||
obj.flags = []; //for debug
|
||||
obj.frames = [];
|
||||
obj.frameStep = {};
|
||||
|
||||
for (var i=0; i<5; i++) {
|
||||
|
||||
obj[prop[i]] = [];
|
||||
var frames = view.getUint16(offset, true);
|
||||
var flags = view.getUint16(offset+2, true);
|
||||
var value = view.getUint16(offset+4, true);
|
||||
var data2 = view.getInt16(offset+6, true)/4096;
|
||||
|
||||
//flags research so far:
|
||||
//bit 13 (8196) - set if inline single frame data, unset if multiple frame data at offset
|
||||
//bit 14-15 - framestep, aka what to shift frame counters by (eg for half framerate this would be 1, frame>>1, essentially dividing the frame speed by 2.)
|
||||
|
||||
obj.frameStep[prop[i]] = (flags>>14);
|
||||
obj.flags[i] = flags;
|
||||
obj.frames[i] = frames;
|
||||
|
||||
if (flags & 8192) {
|
||||
if (value & 32768) value = 65536-value; //convert to int
|
||||
obj[prop[i]].push(value/4096);
|
||||
if (i == 2) obj[prop[i]].push(data2);
|
||||
} else { //data is found at offset
|
||||
frames = frames>>obj.frameStep[prop[i]];
|
||||
//frames -= 1;
|
||||
var off = base + value-8;
|
||||
for (var j=0; j<frames*((i==2)?2:1); j++) {
|
||||
var prevvalue = view.getInt16(off-2, true)/4096;
|
||||
//debugger;
|
||||
obj[prop[i]].push(view.getInt16(off, true)/4096);
|
||||
off += 2;
|
||||
}
|
||||
}
|
||||
|
||||
offset += 8;
|
||||
}
|
||||
obj.nextoff = offset;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
//
|
||||
// nsbtp.js
|
||||
//--------------------
|
||||
// Reads NSBTP files (texture info animation) for use in combination with an NSBMD (model) file
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /formats/nitro.js
|
||||
//
|
||||
|
||||
window.nsbtp = function(input) {
|
||||
|
||||
var mainOff, matOff;
|
||||
var animData;
|
||||
|
||||
//anim data structure:
|
||||
// {
|
||||
// objectData: [
|
||||
// {
|
||||
// obj: { }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
var mainObj = this;
|
||||
var prop = [
|
||||
"scaleS",
|
||||
"scaleT",
|
||||
"rotation",
|
||||
"translateS",
|
||||
"translateT"
|
||||
]
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
var tex;
|
||||
|
||||
//nitro 3d header
|
||||
header = nitro.readHeader(view);
|
||||
if (header.stamp != "BTP0") throw "NSBTP invalid. Expected BTP0, found "+header.stamp;
|
||||
if (header.numSections > 1) throw "NSBTP invalid. Too many sections - should have 1 maximum.";
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
var stamp = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (stamp != "PAT0") throw "NSBTP invalid. Expected PAT0, found "+stamp;
|
||||
|
||||
animData = nitro.read3dInfo(view, mainOff+8, animInfoHandler);
|
||||
debugger;
|
||||
mainObj.animData = animData;
|
||||
}
|
||||
|
||||
function animInfoHandler(view, offset) {
|
||||
var animOff = view.getUint32(offset, true);
|
||||
|
||||
var off = mainOff+animOff;
|
||||
var obj = readAnimData(view, off);
|
||||
obj.nextoff = offset+4;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function readAnimData(view, offset) {
|
||||
matOff = offset;
|
||||
var stamp = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3); //should be M_PT, where _ is a 0 character
|
||||
|
||||
offset += 4;
|
||||
//b400 0303 4400 7400 - countdown (3..2..1.. then start is another model, duration 180 frames, 3 frames of anim)
|
||||
//1400 0404 4800 8800 - kuribo (4 frames, shorter animation duration)
|
||||
//1e00 0202 4000 6000 - pinball stage (2 frames)
|
||||
//0200 0202 4000 6000 - fish, cow and crab (duration and total 2 frames, unusually short animation)
|
||||
//0d00 0404 5000 9000 - bat (duration 13, 6 frames, uneven pacing)
|
||||
|
||||
//16bit duration (60fps frames, total)
|
||||
//8bit tex start
|
||||
//8bit pal start
|
||||
//16bit unknown (flags? kuribo repeats by playing backwards)
|
||||
//16bit unknown
|
||||
|
||||
//example data, for 3 mat 3 pal data
|
||||
//var tinfo = texInfoHandler(view, offset+4);
|
||||
//8 bytes here? looks like texinfo
|
||||
|
||||
var duration = view.getUint16(offset, true);
|
||||
var tframes = view.getUint8(offset+2);
|
||||
var pframes = view.getUint8(offset+3);
|
||||
var unknown = view.getUint16(offset+4, true);
|
||||
var unknown2 = view.getUint16(offset+6, true);
|
||||
|
||||
//...then another nitro
|
||||
var data = nitro.read3dInfo(view, offset+8, matInfoHandler);
|
||||
|
||||
return {data: data, nextoff: data.nextoff, tframes:tframes, pframes:pframes, unknown:unknown, unknown2:unknown2, duration:duration};
|
||||
}
|
||||
|
||||
function matInfoHandler(view, offset, base) {
|
||||
var obj = {}
|
||||
obj.frames = [];
|
||||
|
||||
// in here...
|
||||
// 16bit frames
|
||||
// 16bit maybe material number (probably? mostly 0) to replace
|
||||
// 16bit unknown (flags? 0x4400 count, 0x1101 waluigi, 0x3303 goomba, 0x0010 fish)
|
||||
// 16bit offset from M_PT (always 0x38)
|
||||
|
||||
//at offset (frame of these)
|
||||
// 16bit happenAt
|
||||
// 8bit tex
|
||||
// 8bit pal
|
||||
|
||||
//then (frame of these)
|
||||
// 16char texname
|
||||
//then (frame of these)
|
||||
// 16char palname
|
||||
|
||||
var frames = view.getUint16(offset, true);
|
||||
obj.matinfo = view.getUint16(offset+2, true);
|
||||
obj.flags = view.getUint16(offset+4, true);
|
||||
var offset2 = view.getUint16(offset+6, true);
|
||||
offset += 8;
|
||||
obj.nextoff = offset;
|
||||
|
||||
offset = matOff + offset2;
|
||||
//info and timing for each frame
|
||||
for (var i=0; i<frames; i++) {
|
||||
obj.frames[i] = {
|
||||
time: view.getUint16(offset, true),
|
||||
tex: view.getUint8(offset+2),
|
||||
mat: view.getUint8(offset+3),
|
||||
}
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
//read 16char tex names
|
||||
for (var i=0; i<frames; i++) {
|
||||
|
||||
}
|
||||
//read 16char pal names
|
||||
for (var i=0; i<frames; i++) {
|
||||
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
//
|
||||
// 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) {
|
||||
var texDataSize, texInfoOff, texOffset, compTexSize, compTexInfoOff,
|
||||
compTexOffset, compTexInfoDataOff /*wtf*/, palSize, palInfoOff,
|
||||
palOffset, mainOff
|
||||
|
||||
var textureInfo, paletteInfo, palData, texData, compData, compInfoData, colourBuffer
|
||||
var thisObj = this;
|
||||
|
||||
var bitDepths = [0, 8, 2, 4, 8, 2, 8, 16]
|
||||
|
||||
if (input != null) {
|
||||
load(input, tex0);
|
||||
}
|
||||
this.load = load;
|
||||
this.readTexWithPal = readTexWithPal;
|
||||
this.cache = {}; //textures for btx are cached in this object.
|
||||
|
||||
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
||||
|
||||
function load(input, tex0) {
|
||||
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);
|
||||
texDataSize = view.getUint16(offset+0x0C, true)<<3;
|
||||
texInfoOff = view.getUint16(offset+0x0E, true);
|
||||
texOffset = view.getUint16(offset+0x14, true);
|
||||
|
||||
compTexSize = view.getUint16(offset+0x1C, true)<<3;
|
||||
compTexInfoOff = view.getUint16(offset+0x1E, true);
|
||||
compTexOffset = view.getUint32(offset+0x24, true);
|
||||
compTexInfoDataOff = view.getUint32(offset+0x28, true);
|
||||
|
||||
palSize = view.getUint32(offset+0x30, true)<<3;
|
||||
palInfoOff = view.getUint32(offset+0x34, true);
|
||||
palOffset = view.getUint32(offset+0x38, true);
|
||||
|
||||
//read palletes, then textures.
|
||||
var po = mainOff + palOffset;
|
||||
palData = input.slice(po, po+palSize);
|
||||
|
||||
var to = mainOff + texOffset;
|
||||
texData = input.slice(to, to+texDataSize);
|
||||
|
||||
var co = mainOff + compTexOffset;
|
||||
compData = input.slice(co, co+compTexSize); //pixel information for compression. 2bpp, 16 pixels, so per 4x4 block takes up 4 bytes
|
||||
|
||||
var cio = mainOff + compTexInfoDataOff;
|
||||
compInfoData = input.slice(cio, cio+compTexSize/2); //each 4x4 block has a 16bit information uint. 2 bytes per block, thus half the size of above.
|
||||
|
||||
|
||||
paletteInfo = nitro.read3dInfo(view, mainOff + palInfoOff, palInfoHandler);
|
||||
textureInfo = nitro.read3dInfo(view, mainOff + texInfoOff, texInfoHandler);
|
||||
|
||||
thisObj.paletteInfo = paletteInfo;
|
||||
thisObj.textureInfo = textureInfo;
|
||||
}
|
||||
|
||||
function readTexWithPal(textureId, palId) {
|
||||
var tex = textureInfo.objectData[textureId];
|
||||
var pal = paletteInfo.objectData[palId];
|
||||
|
||||
var format = tex.format;
|
||||
var trans = tex.pal0trans;
|
||||
|
||||
if (format == 5) return readCompressedTex(tex, pal); //compressed 4x4 texture, different processing entirely
|
||||
|
||||
var off = tex.texOffset;
|
||||
var palView = new DataView(palData);
|
||||
var texView = new DataView(texData);
|
||||
var palOff = pal.palOffset;
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = tex.width;
|
||||
canvas.height = tex.height;
|
||||
var ctx = canvas.getContext("2d");
|
||||
var img = ctx.getImageData(0, 0, tex.width, tex.height);
|
||||
|
||||
var total = tex.width*tex.height;
|
||||
var databuf;
|
||||
for (var i=0; i<total; i++) {
|
||||
var col;
|
||||
if (format == 1) { //A3I5 encoding. 3 bits alpha 5 bits pal index
|
||||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&31, trans);
|
||||
col[3] = (dat>>5)*(255/7);
|
||||
|
||||
} else if (format == 2) { //2 bit pal
|
||||
if (i%4 == 0) databuf = texView.getUint8(off++);
|
||||
col = readPalColour(palView, palOff, (databuf>>((i%4)*2))&3, trans)
|
||||
|
||||
} else if (format == 3) { //4 bit pal
|
||||
if (i%2 == 0) {
|
||||
databuf = texView.getUint8(off++);
|
||||
col = readPalColour(palView, palOff, databuf&15, trans)
|
||||
} else {
|
||||
col = readPalColour(palView, palOff, databuf>>4, trans)
|
||||
}
|
||||
|
||||
} else if (format == 4) { //8 bit pal
|
||||
col = readPalColour(palView, palOff, texView.getUint8(off++), trans)
|
||||
|
||||
} else if (format == 6) { //A5I3 encoding. 5 bits alpha 3 bits pal index
|
||||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&7, trans);
|
||||
col[3] = (dat>>3)*(255/31);
|
||||
|
||||
} else if (format == 7) { //raw color data
|
||||
col = texView.getUint16(off, 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);
|
||||
col = colourBuffer;
|
||||
off += 2;
|
||||
|
||||
} else {
|
||||
console.log("texture format is none, ignoring")
|
||||
return canvas;
|
||||
}
|
||||
img.data.set(col, i*4);
|
||||
}
|
||||
ctx.putImageData(img, 0, 0)
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function readCompressedTex(tex, pal) { //format 5, 4x4 texels. I'll keep this well documented so it's easy to understand.
|
||||
var off = tex.texOffset;
|
||||
var texView = new DataView(compData); //real texture data - 32 bits per 4x4 block (one byte per 4px horizontal line, each descending 1px)
|
||||
var compView = new DataView(compInfoData); //view into compression info - informs of pallete and parameters.
|
||||
var palView = new DataView(palData); //view into the texture pallete
|
||||
var compOff = off/2; //info is 2 bytes per block, so the offset is half that of the tex offset.
|
||||
var palOff = pal.palOffset;
|
||||
var transColor = new Uint8Array([0, 0, 0, 0]); //transparent black
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = tex.width;
|
||||
canvas.height = tex.height;
|
||||
var ctx = canvas.getContext("2d");
|
||||
var img = ctx.getImageData(0, 0, tex.width, tex.height);
|
||||
|
||||
var w = tex.width>>2; //iterate over blocks, block w and h is /4.
|
||||
var h = tex.height>>2;
|
||||
|
||||
for (var y=0; y<h; y++) {
|
||||
for (var x=0; x<w; x++) {
|
||||
//inside block
|
||||
var bInfo = compView.getUint16(compOff, true); //block info
|
||||
|
||||
var addr = (bInfo & 0x3fff); //offset to relevant pallete
|
||||
var mode = ((bInfo >> 14) & 3);
|
||||
|
||||
var finalPo = palOff+addr*4;
|
||||
var imgoff = x*4+(y*w*16);
|
||||
for (var iy=0; iy<4; iy++) {
|
||||
var dat = texView.getUint8(off++);
|
||||
for (var ix=0; ix<4; ix++) { //iterate over horiz lines
|
||||
var part = (dat>>(ix*2))&3;
|
||||
var col;
|
||||
|
||||
switch (mode) {
|
||||
case 0: //value 3 is transparent, otherwise pal colour
|
||||
if (part == 3) col = transColor;
|
||||
else col = readPalColour(palView, finalPo, part);
|
||||
break;
|
||||
case 1: //average mode - colour 2 is average of 1st two, 3 is transparent. 0&1 are normal.
|
||||
if (part == 3) col = transColor;
|
||||
else if (part == 2) col = readFractionalPal(palView, finalPo, 0.5);
|
||||
else col = readPalColour(palView, finalPo, part);
|
||||
break;
|
||||
case 2: //pal colour
|
||||
col = readPalColour(palView, finalPo, part);
|
||||
break;
|
||||
case 3: //5/8 3/8 mode - colour 2 is 5/8 of col0 plus 3/8 of col1, 3 is 3/8 of col0 plus 5/8 of col1. 0&1 are normal.
|
||||
if (part == 3) col = readFractionalPal(palView, finalPo, 3/8);
|
||||
else if (part == 2) col = readFractionalPal(palView, finalPo, 5/8);
|
||||
else col = readPalColour(palView, finalPo, part);
|
||||
break;
|
||||
}
|
||||
|
||||
img.data.set(col, (imgoff++)*4)
|
||||
}
|
||||
imgoff += tex.width-4;
|
||||
}
|
||||
compOff += 2; //align off to next block
|
||||
}
|
||||
}
|
||||
|
||||
ctx.putImageData(img, 0, 0)
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function readPalColour(view, palOff, ind, pal0trans) {
|
||||
var col = view.getUint16(palOff+ind*2, true);
|
||||
var f = 255/31;
|
||||
colourBuffer[0] = Math.round((col&31)*f)
|
||||
colourBuffer[1] = Math.round(((col>>5)&31)*f)
|
||||
colourBuffer[2] = Math.round(((col>>10)&31)*f)
|
||||
colourBuffer[3] = (pal0trans && ind == 0)?0:255;
|
||||
return colourBuffer;
|
||||
}
|
||||
|
||||
function readFractionalPal(view, palOff, i) {
|
||||
var col = view.getUint16(palOff, true);
|
||||
var col2 = view.getUint16(palOff+2, true);
|
||||
var ni = 1-i;
|
||||
var f = 255/31;
|
||||
colourBuffer[0] = Math.round((col&31)*f*i + (col2&31)*f*ni)
|
||||
colourBuffer[1] = Math.round(((col>>5)&31)*f*i + ((col2>>5)&31)*f*ni)
|
||||
colourBuffer[2] = Math.round(((col>>10)&31)*f*i + ((col2>>10)&31)*f*ni)
|
||||
colourBuffer[3] = 255;
|
||||
return colourBuffer;
|
||||
}
|
||||
|
||||
function palInfoHandler(view, offset) {
|
||||
var palOffset = view.getUint16(offset, true)<<3;
|
||||
var unknown = view.getUint16(offset+2, true);
|
||||
return {
|
||||
palOffset: palOffset,
|
||||
unknown: unknown,
|
||||
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,
|
||||
pal0trans: (flags>>13)&1, //two top flags are texture matrix modes. not sure if it really matters (except for nsbta animation maybe, but 0 = no transform and things that have tex animations are set to 0 anyways).
|
||||
format: ((flags>>10)&7),
|
||||
height: 8 << ((flags>>7)&7),
|
||||
width: 8 << ((flags>>4)&7),
|
||||
repeatX: flags&1,
|
||||
repeatY: (flags>>1)&1,
|
||||
flipX: (flags>>2)&1,
|
||||
flipY: (flags>>3)&1,
|
||||
|
||||
unkWidth: width2,
|
||||
unk1: unknown,
|
||||
unkHeight: height2,
|
||||
unk2: unknown2,
|
||||
|
||||
nextoff: offset+8
|
||||
}
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
//
|
||||
// sbnk.js
|
||||
//--------------------
|
||||
// Reads sbnk files.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.sbnk = function(input, dataView) {
|
||||
var t = this;
|
||||
this.load = load;
|
||||
|
||||
function load(input, dataView) {
|
||||
var view = (dataView)?input:(new DataView(input));
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
|
||||
var stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
if (stamp != "SBNK") throw "SWAV invalid. Expected SWAV, found "+stamp;
|
||||
offset += 16;
|
||||
var data = readChar(view, offset)+readChar(view, offset+1)+readChar(view, offset+2)+readChar(view, offset+3);
|
||||
if (data != "DATA") throw "SWAV invalid, expected DATA, found "+data;
|
||||
offset += 8;
|
||||
|
||||
offset += 32; //skip reserved
|
||||
|
||||
var numInst = view.getUint32(offset, true);
|
||||
t.instruments = [];
|
||||
offset += 4;
|
||||
for (var i=0; i<numInst; i++) {
|
||||
var fRecord = view.getUint8(offset);
|
||||
var nOffset = view.getUint16(offset+1, true);
|
||||
|
||||
if (fRecord == 0) {
|
||||
t.instruments.push({type:0});
|
||||
} else if (fRecord < 16) { //note/wave definition
|
||||
var obj = readParams(view, nOffset);
|
||||
obj.type = 1;
|
||||
t.instruments.push(obj);
|
||||
} else if (fRecord == 16) {
|
||||
var obj = {};
|
||||
obj.lower = view.getUint8(nOffset++);
|
||||
obj.upper = view.getUint8(nOffset++);
|
||||
obj.entries = []
|
||||
var notes = (obj.upper-obj.lower)+1;
|
||||
for (var j=0; j<notes; j++) {
|
||||
obj.entries.push(readParams(view, nOffset+2));
|
||||
nOffset += 12;
|
||||
}
|
||||
obj.type = 2;
|
||||
t.instruments.push(obj);
|
||||
} else if (fRecord == 17) {
|
||||
var obj = {};
|
||||
obj.regions = [];
|
||||
for (var j=0; j<8; j++) {
|
||||
var dat = view.getUint8(nOffset+j);
|
||||
if (dat != 0) obj.regions.push(dat);
|
||||
else break;
|
||||
}
|
||||
obj.entries = [];
|
||||
nOffset += 8;
|
||||
for (var j=0; j<obj.regions.length; j++) {
|
||||
obj.entries.push(readParams(view, nOffset+2));
|
||||
nOffset += 12;
|
||||
}
|
||||
obj.type = 3;
|
||||
t.instruments.push(obj);
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
function readParams(view, off) {
|
||||
var obj = {};
|
||||
obj.swav = view.getUint16(off, true);
|
||||
obj.swar = view.getUint16(off+2, true);
|
||||
obj.note = view.getUint8(off+4);
|
||||
|
||||
obj.freq = noteToFreq(obj.note);
|
||||
|
||||
obj.attack = view.getUint8(off+5);
|
||||
obj.decay = view.getUint8(off+6);
|
||||
obj.sustainLvl = view.getUint8(off+7);
|
||||
obj.release = view.getUint8(off+8);
|
||||
obj.pan = view.getUint8(off+9);
|
||||
return obj;
|
||||
}
|
||||
|
||||
function noteToFreq(n) {
|
||||
return Math.pow(2, (n-49)/12)*440;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
load(input, dataView);
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
//
|
||||
// sdat.js
|
||||
//--------------------
|
||||
// Reads sdat archives.
|
||||
// Right now this just loads literally every resource in the sdat since in js there is no such thing as half loading a
|
||||
// file from local storage, so why not just load it once and store in a usable format.
|
||||
//
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.sdat = function(input) {
|
||||
var t = this;
|
||||
this.sections = {};
|
||||
this.load = load;
|
||||
|
||||
function load(input) {
|
||||
t.buffer = input;
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
|
||||
var stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
if (stamp != "SDAT") throw "SDAT invalid. Expected SDAT, found "+stamp;
|
||||
|
||||
var unknown1 = view.getUint32(0x4, true);
|
||||
var filesize = view.getUint32(0x8, true);
|
||||
var headsize = view.getUint16(0xC, true);
|
||||
var numSections = view.getUint16(0xE, true);
|
||||
var sectionOffsets = [];
|
||||
var sectionSizes = [];
|
||||
for (var i=3; i>-1; i--) { //reverse order so we can process files into js objects
|
||||
var off = (view.getUint32(0x10+i*8, true));
|
||||
var size = (view.getUint32(0x14+i*8, true));
|
||||
if (size != 0) readSection(view, off);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function readSection(view, off) {
|
||||
var stamp = "$"+readChar(view, off)+readChar(view, off+1)+readChar(view, off+2)+readChar(view, off+3);
|
||||
if (sectionFunc[stamp] != null) t.sections[stamp] = sectionFunc[stamp](view, off+8);
|
||||
else console.error("Invalid section in SDAT! No handler for section type "+stamp.substr(1, 4));
|
||||
}
|
||||
|
||||
var sectionFunc = {}
|
||||
|
||||
sectionFunc["$SYMB"] = function(view, off) {
|
||||
|
||||
}
|
||||
|
||||
sectionFunc["$INFO"] = function(view, off) {
|
||||
var obj = [];
|
||||
for (var i=0; i<8; i++) {
|
||||
var relOff = off+view.getUint32(off+i*4, true)-8;
|
||||
|
||||
var count = view.getUint32(relOff, true);
|
||||
obj[i] = [];
|
||||
relOff += 4;
|
||||
var last = null;
|
||||
for (var j=0; j<count; j++) {
|
||||
var infoOff = view.getUint32(relOff, true);
|
||||
//WRONG
|
||||
last = recordInfoFunc[i](view, off+infoOff-8);//(infoOff == 0 && last != null)?last.nextOff:(off+infoOff-8));
|
||||
obj[i][j] = last;
|
||||
relOff += 4;
|
||||
}
|
||||
}
|
||||
console.log(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
sectionFunc["$FAT "] = function(view, off) {
|
||||
var a = [];
|
||||
var count = view.getUint32(off, true);
|
||||
off += 4;
|
||||
for (var i=0; i<count; i++) {
|
||||
var obj = {};
|
||||
obj.off = view.getUint32(off, true);
|
||||
obj.size = view.getUint32(off+4, true);
|
||||
off += 16;
|
||||
a.push(obj);
|
||||
}
|
||||
console.log(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
sectionFunc["$FILE"] = function(view, off) {
|
||||
console.log("file");
|
||||
}
|
||||
|
||||
var recordInfoFunc = []
|
||||
|
||||
recordInfoFunc[0] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.fileID = view.getUint16(off, true);
|
||||
obj.seq = new sseq(getFile(obj.fileID));
|
||||
obj.pc = 0;
|
||||
obj.unknown = view.getUint16(off+2, true);
|
||||
obj.bank = view.getUint16(off+4, true);
|
||||
obj.vol = view.getUint8(off+6);
|
||||
obj.cpr = view.getUint8(off+7);
|
||||
obj.ppr = view.getUint8(off+8);
|
||||
obj.ply = view.getUint8(off+9);
|
||||
obj.nextOff = off+10;
|
||||
return obj;
|
||||
}
|
||||
recordInfoFunc[1] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.fileID = view.getUint16(off, true);
|
||||
obj.arc = new ssar(getFile(obj.fileID));
|
||||
obj.unknown = view.getUint16(off+2, true);
|
||||
obj.nextOff = off+4;
|
||||
return obj;
|
||||
}
|
||||
recordInfoFunc[2] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.fileID = view.getUint16(off, true);
|
||||
obj.unknown = view.getUint16(off+2, true);
|
||||
obj.bank = new sbnk(getFile(obj.fileID));
|
||||
obj.waveArcs = [];
|
||||
off += 4;
|
||||
for (var i=0; i<4; i++) {
|
||||
obj.waveArcs[i] = view.getUint16(off, true);
|
||||
off += 2;
|
||||
}
|
||||
obj.nextOff = off;
|
||||
return obj;
|
||||
}
|
||||
recordInfoFunc[3] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.fileID = view.getUint16(off, true);
|
||||
obj.unknown = view.getUint16(off+2, true);
|
||||
obj.arc = new swar(getFile(obj.fileID));
|
||||
obj.nextOff = off+4;
|
||||
return obj;
|
||||
}
|
||||
recordInfoFunc[4] = function(view, off) {}
|
||||
recordInfoFunc[5] = function(view, off) {}
|
||||
recordInfoFunc[6] = function(view, off) {}
|
||||
|
||||
recordInfoFunc[7] = function(view, off) {
|
||||
var obj = {};
|
||||
obj.fileID = view.getUint16(off, true);
|
||||
obj.unknown = view.getUint16(off+2, true);
|
||||
obj.vol = view.getUint8(off+4);
|
||||
obj.pri = view.getUint8(off+5);
|
||||
obj.ply = view.getUint8(off+6);
|
||||
obj.nextOff = off+7;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function getFile(fid) {
|
||||
var file = t.sections["$FAT "][fid];
|
||||
if (file != null) {
|
||||
return t.buffer.slice(file.off, file.off+file.size);
|
||||
}
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
//
|
||||
// ssar.js
|
||||
//--------------------
|
||||
// Reads ssar files.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.ssar = function(input) {
|
||||
var t = this;
|
||||
this.load = load;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
|
||||
var stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
if (stamp != "SSAR") throw "SSAR invalid. Expected SSAR, found "+stamp;
|
||||
offset += 16;
|
||||
var data = readChar(view, offset)+readChar(view, offset+1)+readChar(view, offset+2)+readChar(view, offset+3);
|
||||
if (data != "DATA") throw "SSAR invalid, expected DATA, found "+data;
|
||||
offset += 8;
|
||||
|
||||
t.dataOff = view.getUint32(offset, true);
|
||||
t.data = new Uint8Array(view.buffer.slice(t.dataOff));
|
||||
var count = view.getUint32(offset+4, true);
|
||||
t.entries = [];
|
||||
|
||||
offset += 8;
|
||||
for (var i=0; i<count; i++) {
|
||||
t.entries.push(readSeqEntry(view, offset));
|
||||
offset += 12;
|
||||
}
|
||||
}
|
||||
|
||||
function readSeqEntry(view, off) {
|
||||
var obj = {};
|
||||
obj.pc = view.getUint32(off, true);
|
||||
obj.seq = {data:t.data};
|
||||
obj.bank = view.getUint16(off+4, true);
|
||||
obj.vol = view.getUint8(off+6);
|
||||
obj.cpr = view.getUint8(off+7);
|
||||
obj.ppr = view.getUint8(off+8);
|
||||
obj.ply = view.getUint8(off+9);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// sseq.js
|
||||
//--------------------
|
||||
// Reads sseq files.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.sseq = function(input) {
|
||||
var t = this;
|
||||
this.load = load;
|
||||
|
||||
function load(input, archived) {
|
||||
var view = new DataView(input);
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
|
||||
var stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
if (stamp != "SSEQ") throw "SSEQ invalid. Expected SSEQ, found "+stamp;
|
||||
offset += 16;
|
||||
var data = readChar(view, offset)+readChar(view, offset+1)+readChar(view, offset+2)+readChar(view, offset+3);
|
||||
if (data != "DATA") throw "SWAV invalid, expected DATA, found "+data;
|
||||
offset += 8;
|
||||
|
||||
t.data = new Uint8Array(input.slice(view.getUint32(offset, true)));
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
//
|
||||
// swar.js
|
||||
//--------------------
|
||||
// Reads swar files.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.swar = function(input, dataView) {
|
||||
var t = this;
|
||||
this.load = load;
|
||||
|
||||
function load(input, dataView) {
|
||||
var view = (dataView)?input:(new DataView(input));
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
|
||||
var stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
if (stamp != "SWAR") throw "SWAR invalid. Expected SWAR, found "+stamp;
|
||||
offset += 16; //skip magic number, size and number of blocks
|
||||
var data = readChar(view, offset)+readChar(view, offset+1)+readChar(view, offset+2)+readChar(view, offset+3);
|
||||
if (data != "DATA") throw "SWAV invalid, expected DATA, found "+data;
|
||||
offset += 40; //skip reserved 0s and size
|
||||
|
||||
var nSamples = view.getUint32(offset, true);
|
||||
offset += 4;
|
||||
|
||||
t.samples = [];
|
||||
for (var i=0; i<nSamples; i++) {
|
||||
t.samples.push(new swav(new DataView(input, view.getUint32(offset, true)), false, true));
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
load(input, dataView);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
//
|
||||
// swav.js
|
||||
//--------------------
|
||||
// Reads swav files.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
//
|
||||
|
||||
window.swav = function(input, hasHead, dataView) {
|
||||
var t = this;
|
||||
this.load = load;
|
||||
this.getAudioBuffer = getAudioBuffer;
|
||||
|
||||
function load(input, hasHead, dataView) {
|
||||
var view = (dataView)?input:(new DataView(input));
|
||||
var header = null;
|
||||
var offset = 0;
|
||||
|
||||
if (hasHead) {
|
||||
var stamp = readChar(view, 0x0)+readChar(view, 0x1)+readChar(view, 0x2)+readChar(view, 0x3);
|
||||
if (stamp != "SWAV") throw "SWAV invalid. Expected SWAV, found "+stamp;
|
||||
offset += 16;
|
||||
var data = readChar(view, offset)+readChar(view, offset+1)+readChar(view, offset+2)+readChar(view, offset+3);
|
||||
if (data != "DATA") throw "SWAV invalid, expected DATA, found "+data;
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
t.waveType = view.getUint8(offset);
|
||||
t.bLoop = view.getUint8(offset+1);
|
||||
t.nSampleRate = view.getUint16(offset+2, true);
|
||||
if (t.nSampleRate < 3000) throw "BAD SAMPLE RATE! "+t.nSampleRate;
|
||||
t.nTime = view.getUint16(offset+4, true);
|
||||
t.nLoopOff = view.getUint16(offset+6, true);
|
||||
t.nNonLoopLen = view.getUint32(offset+8, true);
|
||||
t.bytesize = (t.nLoopOff+t.nNonLoopLen)*4;
|
||||
t.mul = 1;
|
||||
|
||||
offset += 12;
|
||||
var data;
|
||||
switch (t.waveType) {
|
||||
case 0:
|
||||
var size = t.bytesize;
|
||||
data = new Float32Array(size);
|
||||
for (var i=0; i<size; i++) {
|
||||
data[i] = view.getInt8(offset++)/0x7F;
|
||||
}
|
||||
t.loopSTime = (t.nLoopOff*4)/t.nSampleRate;
|
||||
break;
|
||||
case 1:
|
||||
var size = t.bytesize/2;
|
||||
data = new Float32Array(size);
|
||||
for (var i=0; i<size; i++) {
|
||||
data[i] = view.getInt16(offset, true)/0x7FFF;
|
||||
offset += 2;
|
||||
}
|
||||
t.loopSTime = (t.nLoopOff*2)/t.nSampleRate;
|
||||
break;
|
||||
case 2:
|
||||
data = decodeADPCM(view, offset);
|
||||
t.loopSTime = ((t.nLoopOff-1)*8)/t.nSampleRate;
|
||||
break;
|
||||
}
|
||||
t.data = data;
|
||||
}
|
||||
|
||||
function getAudioBuffer(ctx) {
|
||||
while (true) {
|
||||
try {
|
||||
var buf = ctx.createBuffer(1, t.data.length, t.nSampleRate);
|
||||
buf.getChannelData(0).set(t.data);
|
||||
return buf;
|
||||
} catch (e) {
|
||||
t.nSampleRate *= 2; //keep increasing sample rate until the target supports it.
|
||||
t.loopSTime /= 2;
|
||||
t.mul *= 2;
|
||||
}
|
||||
if (t.nSampleRate > 96000) return ctx.createBuffer(1, 1, 44000); //give up and return an empty buffer
|
||||
}
|
||||
}
|
||||
|
||||
var indChanges = [-1, -1, -1, -1, 2, 4, 6, 8];
|
||||
var ADPCMTable = [
|
||||
0x0007,0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x0010,0x0011,0x0013,0x0015,
|
||||
0x0017,0x0019,0x001C,0x001F,0x0022,0x0025,0x0029,0x002D,0x0032,0x0037,0x003C,0x0042,
|
||||
0x0049,0x0050,0x0058,0x0061,0x006B,0x0076,0x0082,0x008F,0x009D,0x00AD,0x00BE,0x00D1,
|
||||
0x00E6,0x00FD,0x0117,0x0133,0x0151,0x0173,0x0198,0x01C1,0x01EE,0x0220,0x0256,0x0292,
|
||||
0x02D4,0x031C,0x036C,0x03C3,0x0424,0x048E,0x0502,0x0583,0x0610,0x06AB,0x0756,0x0812,
|
||||
0x08E0,0x09C3,0x0ABD,0x0BD0,0x0CFF,0x0E4C,0x0FBA,0x114C,0x1307,0x14EE,0x1706,0x1954,
|
||||
0x1BDC,0x1EA5,0x21B6,0x2515,0x28CA,0x2CDF,0x315B,0x364B,0x3BB9,0x41B2,0x4844,0x4F7E,
|
||||
0x5771,0x602F,0x69CE,0x7462,0x7FFF
|
||||
]; //thanks no$gba docs
|
||||
|
||||
function decodeADPCM(view, off) {
|
||||
var pcm = view.getUint16(off, true); //initial pcm
|
||||
var ind = view.getUint8(off+2); //initial index
|
||||
off += 4;
|
||||
|
||||
var size = t.bytesize-4;
|
||||
var out = new Float32Array((size*2));
|
||||
var write = 0;
|
||||
//out[write++] = pcm/0x7FFF;
|
||||
|
||||
for (var i=0; i<size; i++) {
|
||||
var b = view.getUint8(off++);
|
||||
for (var j=0; j<2; j++) {
|
||||
var nibble = (b>>(j*4))&15;
|
||||
|
||||
var diff = Math.floor(((nibble&7)*2+1)*ADPCMTable[ind]/8);
|
||||
if (nibble&8) pcm = Math.max(pcm-diff, -0x7FFF);
|
||||
else pcm = Math.min(pcm+diff, 0x7FFF);
|
||||
out[write++] = pcm/0x7FFF;
|
||||
|
||||
ind = Math.min(88, Math.max(0, ind + indChanges[nibble&7]));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
load(input, hasHead, dataView);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user