All courses now render properly. WIP items (shell, banana, fake item)
This commit is contained in:
1 parent
e010627b5d
commit
ff21142744
45 files changed
+3400
-1193
No files matched your search
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// ncer.js
|
||||
//--------------------
|
||||
// Loads ncer files and provides a variety of functions for accessing and using the data.
|
||||
// Cell data for nitro 2d graphics. Multiple images made out of multiple cells, sharing an input palette and ncgr.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.ncer = function(input) {
|
||||
var dimensions = [ //indexed by width, then height
|
||||
[[8,8], [16,8], [8,16]],
|
||||
[[16,16], [32,8], [8,32]],
|
||||
[[32,32], [32,16], [16,32]],
|
||||
[[64,64], [64,32], [32,64]],
|
||||
]
|
||||
|
||||
var mainOff;
|
||||
var t = 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 != "RECN") throw "NCER invalid. Expected RECN, found "+header.stamp;
|
||||
if (header.numSections < 1 || header.numSections > 3) throw "NCER invalid. Too many sections - should have 1-3.";
|
||||
//end nitro
|
||||
t.sectionOffsets = header.sectionOffsets;
|
||||
t.sectionOffsets[0] = 0x18;
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
t.cebk = loadCEBK(view);
|
||||
/* ignore for now
|
||||
t.labl = loadLABL(view);
|
||||
t.uext = loadUEXT(view); //external data?
|
||||
*/
|
||||
}
|
||||
|
||||
function getSize(shape, size, double) {
|
||||
var dim = dimensions[size][shape];
|
||||
if (double) return [dim[0]*2, dim[1]*2];
|
||||
return [dim[0], dim[1]];
|
||||
}
|
||||
|
||||
function readOAM(view, offset) {
|
||||
//see ds docs. really, any of them?
|
||||
var obj0 = view.getUint16(offset, true);
|
||||
var obj1 = view.getUint16(offset + 2, true);
|
||||
var obj2 = view.getUint16(offset + 4, true);
|
||||
|
||||
var rsFlag = (obj0 & 0x100) > 0;
|
||||
var x = obj1 & 0x01FF;
|
||||
var y = obj0 & 0xFF;
|
||||
var result = {
|
||||
y: (y > 0x80) ? y-0x100 : y,
|
||||
rsFlag: rsFlag,
|
||||
disable: (!rsFlag && (obj0 & 0x200) > 0),
|
||||
doubleSize: (rsFlag && (obj0 & 0x200) > 0),
|
||||
objMode: (obj0 >> 10) & 0x3,
|
||||
mosaic: (obj0 & 0x1000) > 0,
|
||||
depth: (obj0 & 0x2000) > 0,
|
||||
shape: (obj0 >> 14) & 0x3, //used in combination with size to determine final x+y tile size.
|
||||
|
||||
x: (x > 0x100) ? x-0x200 : x,
|
||||
|
||||
xFlip: (!rsFlag && (obj1 & 0x1000) > 0),
|
||||
yFlip: (!rsFlag && (obj1 & 0x2000) > 0),
|
||||
|
||||
selectParam: rsFlag ? ((obj1 >> 9) & 0x1F) : 0,
|
||||
|
||||
size: (obj1 >> 14) & 0x3,
|
||||
|
||||
tileOffset: obj2 & 0x03FF,
|
||||
priority: (obj2 >> 10) & 3,
|
||||
pal: (obj2 >> 12) & 0xF,
|
||||
}
|
||||
result.size = getSize(result.shape, result.size, result.doubleSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
function loadCEBK(view) { //cell bank
|
||||
var offset = t.sectionOffsets[0] - 8;
|
||||
|
||||
var cebk = {};
|
||||
cebk.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (cebk.type != "KBEC") throw "NCER invalid. Expected KBEC, found "+cebk.type;
|
||||
cebk.blockSize = view.getUint32(offset+0x4, true);
|
||||
cebk.imageCount = view.getUint16(offset+0x8, true);
|
||||
cebk.bankType = view.getUint16(offset+0xA, true); //type 1 has additional fields
|
||||
cebk.unknown = view.getUint32(offset+0xC, true); //always 0x12
|
||||
cebk.boundarySize = view.getUint32(offset+0x10, true) * 64; //area in which the image can be drawn (pixel height AND width)
|
||||
cebk.partitionDataOffset = view.getUint32(offset+0x14, true);
|
||||
//pad 0x18
|
||||
//pad 0x1C
|
||||
|
||||
cebk.images = [];
|
||||
|
||||
offset += 0x20;
|
||||
var tableEnd = offset + cebk.imageCount * (8 + cebk.bankType * 8);
|
||||
for (var i=0; i<cebk.imageCount; i++) {
|
||||
var image = {};
|
||||
image.numCells = view.getUint16(offset, true);
|
||||
image.readOnlyInfo = view.getUint16(offset + 0x2, true);
|
||||
image.offset = view.getInt32(offset + 0x4, true);
|
||||
offset += 0x8;
|
||||
if (cebk.bankType == 1) {
|
||||
image.xMax = view.getInt16(offset, true);
|
||||
image.yMax = view.getInt16(offset+2, true);
|
||||
image.xMin = view.getInt16(offset+4, true);
|
||||
image.yMin = view.getInt16(offset+6, true);
|
||||
offset += 0x8;
|
||||
}
|
||||
|
||||
var offset2 = tableEnd + image.offset;
|
||||
image.cells = [];
|
||||
for (var j=0; j<image.numCells; j++) {
|
||||
var cell = readOAM(view, offset2);
|
||||
offset2 += 6;
|
||||
image.cells.push(cell);
|
||||
}
|
||||
cebk.images.push(image);
|
||||
}
|
||||
|
||||
if (cebk.partitionDataOffset != 0) { //not sure what this does, just that it's here
|
||||
var pOff = t.sectionOffsets[0] + cebk.partitionDataOffset;
|
||||
cebk.maxPartitionSize = view.getUint32(pOff, true);
|
||||
cebk.firstOffset = view.getUint32(pOff+4, true);
|
||||
pOff += cebk.firstOffset;
|
||||
for (var i=0; i<cebk.imageCount; i++) {
|
||||
cebk.images[i].partitionOffset = view.getUint32(pOff, true);
|
||||
cebk.images[i].partitionSize = view.getUint32(pOff+4, true);
|
||||
pOff += 8;
|
||||
}
|
||||
}
|
||||
|
||||
return cebk;
|
||||
}
|
||||
|
||||
function readPalColour(view, ind) {
|
||||
var col = view.getUint16(ind, true);
|
||||
var f = 255/31;
|
||||
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// ncgr.js
|
||||
//--------------------
|
||||
// Loads ncgr files and provides a variety of functions for accessing and using the data.
|
||||
// "Graphics Resource", as in tile data. Usually rendered in conjunction with Palette (nclr) and Cell (ncer) / screen (nscr) data.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.ncgr = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var t = 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 != "RGCN") throw "NCGR invalid. Expected RGCN, found "+header.stamp;
|
||||
if (header.numSections < 1 || header.numSections > 2) throw "NCGR invalid. Too many sections - should have 2.";
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
t.sectionOffsets = header.sectionOffsets;
|
||||
t.sectionOffsets[0] = 0x18;
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
t.char = loadCHAR(view);
|
||||
if (header.numSections > 1) t.cpos = loadCPOS(view);
|
||||
}
|
||||
|
||||
function loadCHAR(view) {
|
||||
var offset = t.sectionOffsets[0] - 8;
|
||||
|
||||
var char = {};
|
||||
char.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (char.type != "RAHC") throw "NCGR invalid. Expected RAHC, found "+char.type;
|
||||
char.blockSize = view.getUint32(offset+0x4, true);
|
||||
t.sectionOffsets[1] = t.sectionOffsets[0] + char.blockSize;
|
||||
char.tilesY = view.getUint16(offset+0x8, true); //(tiles y)
|
||||
char.tilesX = view.getUint16(offset+0xA, true); //(tiles x)
|
||||
char.bitDepth = view.getUint32(offset+0xC, true); //3 - 4bits, 4 - 8bits
|
||||
//pad 0x10
|
||||
char.tiledFlag = view.getUint32(offset+0x14, true);
|
||||
char.tileDataSize = view.getUint32(offset+0x18, true);
|
||||
char.unknown = view.getUint32(offset+0x1C, true); //usually 24
|
||||
offset += 0x20;
|
||||
|
||||
//tiles are 8 or 4 bit index to pal data
|
||||
//64 pixels per tile (8*8)
|
||||
var tileCount = (char.blockSize-0x20) / ((char.bitDepth == 4) ? 64 : 32);
|
||||
char.tiles = [];
|
||||
for (var i=0; i<tileCount; i++) {
|
||||
var tile = [];
|
||||
if (char.bitDepth == 4) {
|
||||
//easy, just read 1024 bytes
|
||||
for (var j=0; j<64; j++) tile.push(view.getUint8(offset++));
|
||||
} else {
|
||||
for (var j=0; j<32; j++) {
|
||||
var dat = view.getUint8(offset++);
|
||||
tile.push(dat&0xF);
|
||||
tile.push(dat>>4);
|
||||
}
|
||||
}
|
||||
char.tiles.push(tile);
|
||||
}
|
||||
return char;
|
||||
}
|
||||
|
||||
function loadCPOS(view) { //palette count map, supposedly. maps each palette to an ID
|
||||
var offset = t.sectionOffsets[1] - 8;
|
||||
|
||||
var cpos = {};
|
||||
cpos.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (cpos.type != "SOPC") throw "NCLR invalid. Expected SOPC, found "+stamp;
|
||||
cpos.blockSize = view.getUint32(offset+0x4, true);
|
||||
//padding 0x8
|
||||
|
||||
cpos.tileSize = view.getUint16(offset+0xC, true); //always 32
|
||||
cpos.tileCount = view.getUint16(offset+0xE, true);
|
||||
return cpos;
|
||||
}
|
||||
|
||||
function readPalColour(view, ind) {
|
||||
var col = view.getUint16(ind, true);
|
||||
var f = 255/31;
|
||||
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// nclr.js
|
||||
//--------------------
|
||||
// Loads nclr files and provides a variety of functions for accessing and using the data.
|
||||
// Palette information for nitro 2d graphics.
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.nclr = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var t = 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 != "RLCN") throw "NCLR invalid. Expected RLCN, found "+header.stamp;
|
||||
if (header.numSections < 1 || header.numSections > 2) throw "NCLR invalid. Too many sections - should have 2.";
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
t.sectionOffsets = header.sectionOffsets;
|
||||
t.sectionOffsets[0] = 0x18;
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
t.pltt = loadPLTT(view);
|
||||
if (header.numSections > 1) t.pcmp = loadPCMP(view);
|
||||
}
|
||||
|
||||
function loadPLTT(view) {
|
||||
var offset = t.sectionOffsets[0] - 8;
|
||||
|
||||
var pltt = {};
|
||||
pltt.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (pltt.type != "TTLP") throw "NCLR invalid. Expected TTLP, found "+pltt.type;
|
||||
pltt.blockSize = view.getUint32(offset+0x4, true);
|
||||
t.sectionOffsets[1] = t.sectionOffsets[0] + pltt.blockSize;
|
||||
pltt.bitDepth = view.getUint32(offset+0x8, true); //3 -> 4bit, 4 -> 8bit
|
||||
pltt.padding = view.getUint32(offset+0xC, true);
|
||||
pltt.palEntries = view.getUint32(offset+0x10, true) / 2; //stored in bytes, 2 bytes per col. seems to be wrong sometimes? (8bit mode, padding as 1)
|
||||
pltt.colorsPerPal = view.getUint32(offset+0x14, true); //usually 16
|
||||
|
||||
//16-bit pallete data
|
||||
//XBBBBBGGGGGRRRRR
|
||||
|
||||
var colsPerPal = (pltt.bitDepth == 4)?256:16;
|
||||
var realPalCount = (pltt.blockSize - 0x18) / 2;
|
||||
|
||||
offset += 0x18;
|
||||
pltt.palettes = [];
|
||||
var curPal = [];
|
||||
for (var i=0; i<realPalCount; i++) {
|
||||
curPal.push(readPalColour(view, offset));
|
||||
if (curPal.length >= colsPerPal) {
|
||||
pltt.palettes.push(curPal);
|
||||
curPal = [];
|
||||
}
|
||||
offset += 2;
|
||||
}
|
||||
if (curPal.length > 0) pltt.palettes.push(curPal);
|
||||
return pltt;
|
||||
}
|
||||
|
||||
function loadPCMP(view) { //palette count map, supposedly. maps each palette to an ID
|
||||
var offset = t.sectionOffsets[1] - 8;
|
||||
|
||||
var pcmp = {};
|
||||
pcmp.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (pcmp.type != "PMCP") throw "NCLR invalid. Expected PMCP, found "+stamp;
|
||||
pcmp.blockSize = view.getUint32(offset+0x4, true);
|
||||
pcmp.palCount = view.getUint16(offset+0x8, true);
|
||||
//unknown 16: 0?
|
||||
pcmp.unknown = view.getUint32(offset+0xC, true);
|
||||
|
||||
offset += 0x10;
|
||||
var palIDs = [];
|
||||
for (var i=0; i<pcmp.palCount; i++) {
|
||||
palIDs.push(view.getUint16(offset, true));
|
||||
offset += 2;
|
||||
}
|
||||
return pcmp;
|
||||
}
|
||||
|
||||
function readPalColour(view, ind) {
|
||||
var col = view.getUint16(ind, true);
|
||||
var f = 255/31;
|
||||
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// nscr.js
|
||||
//--------------------
|
||||
// Loads nscr files and provides a variety of functions for accessing and using the data.
|
||||
// Screen data for nitro 2d graphics. Each cell references a graphic (ncgr) and palette (nclr).
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
window.nscr = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var t = 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 != "RCSN") throw "NSCR invalid. Expected RCSN, found "+header.stamp;
|
||||
if (header.numSections != 1) throw "NSCR invalid. Too many sections - should have 1.";
|
||||
offset = header.sectionOffsets[0];
|
||||
//end nitro
|
||||
t.sectionOffsets = header.sectionOffsets;
|
||||
t.sectionOffsets[0] = 0x18;
|
||||
|
||||
mainOff = offset;
|
||||
|
||||
t.scrn = loadSCRN(view);
|
||||
}
|
||||
|
||||
function loadSCRN(view) {
|
||||
var offset = t.sectionOffsets[0] - 8;
|
||||
|
||||
var scrn = {};
|
||||
scrn.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
if (scrn.type != "NRCS") throw "SCRN invalid. Expected NRCS, found "+scrn.type;
|
||||
scrn.blockSize = view.getUint32(offset+0x4, true);
|
||||
t.sectionOffsets[1] = t.sectionOffsets[0] + scrn.blockSize;
|
||||
scrn.screenWidth = view.getUint16(offset+0x8, true); //in pixels
|
||||
scrn.screenHeight = view.getUint16(offset+0xA, true);
|
||||
scrn.padding = view.getUint32(offset+0xC, true); //always 0
|
||||
scrn.screenDataSize = view.getUint32(offset+0x10, true);
|
||||
offset += 0x14;
|
||||
|
||||
var entries = (scrn.blockSize - 0x14)/2;
|
||||
scrn.data = [];
|
||||
|
||||
for (var i=0; i<entries; i++) {
|
||||
scrn.data.push(view.getUint16(offset, true));
|
||||
offset += 2;
|
||||
}
|
||||
return scrn;
|
||||
|
||||
/*
|
||||
Format is (YYYYXXNNNNNNNNNN)
|
||||
Y4 Palette Number
|
||||
X2 Transformation (YFlip/XFlip)
|
||||
N10 Tile Number
|
||||
*/
|
||||
}
|
||||
|
||||
function readPalColour(view, ind) {
|
||||
var col = view.getUint16(ind, true);
|
||||
var f = 255/31;
|
||||
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,6 @@ window.kartoffsetdata = function(input) {
|
||||
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);
|
||||
|
||||
+8
-7
@@ -84,7 +84,7 @@ window.kcl = function(input, mkwii) {
|
||||
while (offset < octreeOffset) {
|
||||
planes.push(new Plane(view, offset));
|
||||
offset += 0x10;
|
||||
var vert = planes[planes.length-1].Vertex1;
|
||||
var vert = planes[planes.length-1].Vertices[0];
|
||||
if (vert[0] < minx) minx=vert[0];
|
||||
if (vert[0] > maxx) maxx=vert[0];
|
||||
if (vert[2] < minz) minz=vert[2];
|
||||
@@ -177,9 +177,9 @@ window.kcl = function(input, mkwii) {
|
||||
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.moveTo(plane.Vertices[0][0], plane.Vertices[0][2]);
|
||||
ctx.lineTo(plane.Vertices[1][0], plane.Vertices[1][2]);
|
||||
ctx.lineTo(plane.Vertices[2][0], plane.Vertices[2][2]);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
@@ -253,7 +253,8 @@ window.kcl = function(input, mkwii) {
|
||||
|
||||
function Plane(view, offset) {
|
||||
this.Len = readBigDec(view, offset, mkwiiMode);
|
||||
this.Vertex1 = readVert(view.getUint16(offset+0x4, end), view);
|
||||
this.Vertices = [];
|
||||
this.Vertices[0] = 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);
|
||||
@@ -263,8 +264,8 @@ window.kcl = function(input, mkwii) {
|
||||
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)));
|
||||
this.Vertices[1] = vec3.scaleAndAdd(vec3.create(), this.Vertices[0], crossB, (this.Len/vec3.dot(crossB, this.NormalC)));
|
||||
this.Vertices[2] = vec3.scaleAndAdd(vec3.create(), this.Vertices[0], crossA, (this.Len/vec3.dot(crossA, this.NormalC)));
|
||||
}
|
||||
|
||||
function readVert(num, view) {
|
||||
|
||||
+40
-2
@@ -5,10 +5,34 @@
|
||||
// by RHY3756547
|
||||
//
|
||||
|
||||
// for reading from multiple narcs as one. (eg. Race.narc, Race_us.narc)
|
||||
window.narcGroup = function(files) {
|
||||
this.getFile = getFile;
|
||||
this.list = list;
|
||||
|
||||
function getFile(name) {
|
||||
for (var i=0; i<files.length; i++) {
|
||||
var file = files[i].getFile(name);
|
||||
if (file != null) return file;
|
||||
}
|
||||
console.error("File not found: "+name);
|
||||
return null;
|
||||
}
|
||||
|
||||
function list() {
|
||||
var result = [];
|
||||
for (var i=0; i<files.length; i++) {
|
||||
files[i].list(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
window.narc = function(input) {
|
||||
|
||||
this.load = load;
|
||||
this.getFile = getFile;
|
||||
this.list = list;
|
||||
|
||||
var arc = this;
|
||||
var handlers = [];
|
||||
@@ -18,8 +42,6 @@ window.narc = function(input) {
|
||||
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.
|
||||
|
||||
@@ -84,6 +106,22 @@ window.narc = function(input) {
|
||||
return null; //incomplete path; we ended on a directory, not a file!
|
||||
}
|
||||
|
||||
function list(files, curDir, path) {
|
||||
path = path || "/";
|
||||
files = files || [];
|
||||
var table = arc.sections["BTNF"].directories;
|
||||
curDir = curDir || table[0].entries; //root
|
||||
|
||||
for (var i=0; i<curDir.length; i++) {
|
||||
if (curDir[i].dir) {
|
||||
list(files, table[curDir[i].id-0xF000].entries, path+curDir[i].name+"/");
|
||||
} else {
|
||||
files.push(path + curDir[i].name);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function readFileWithID(id) {
|
||||
var table = arc.sections["BTAF"].files;
|
||||
var file = table[id];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
window.ndsFS = function(input) {
|
||||
this.load = load;
|
||||
this.getFile = getFile;
|
||||
this.list = list;
|
||||
|
||||
var arc = this;
|
||||
var handlers = [];
|
||||
@@ -65,6 +66,22 @@ window.ndsFS = function(input) {
|
||||
return null; //incomplete path; we ended on a directory, not a file!
|
||||
}
|
||||
|
||||
function list(files, curDir, path) {
|
||||
path = path || "/";
|
||||
files = files || [];
|
||||
var table = arc.sections["BTNF"].directories;
|
||||
curDir = curDir || table[0].entries; //root
|
||||
|
||||
for (var i=0; i<curDir.length; i++) {
|
||||
if (curDir[i].dir) {
|
||||
list(files, table[curDir[i].id-0xF000].entries, path+curDir[i].name+"/");
|
||||
} else {
|
||||
files.push(path + curDir[i].name);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function readFileWithID(id) {
|
||||
var off = arc.fileOff+id*8;
|
||||
/*var table = arc.sections["BTAF"].files;
|
||||
|
||||
+247
-3
@@ -11,13 +11,17 @@
|
||||
window.nftr = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var mainObj = this;
|
||||
var t = this;
|
||||
this.info = {};
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
this.drawToCanvas = drawToCanvas;
|
||||
this.measureText = measureText;
|
||||
this.measureMapped = measureMapped;
|
||||
this.mapText = mapText;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
@@ -29,9 +33,249 @@ window.nftr = function(input) {
|
||||
header = nitro.readHeader(view);
|
||||
//debugger;
|
||||
if (header.stamp != "RTFN") throw "NFTR invalid. Expected RTFN, found "+header.stamp;
|
||||
offset = header.sectionOffsets[0];
|
||||
offset = 0x10; //nitro header for nftr doesn't have section offsets - they are in order
|
||||
//end nitro
|
||||
|
||||
var info = t.info;
|
||||
info.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
info.blockSize = view.getUint32(offset+0x4, true);
|
||||
|
||||
info.unknown1 = view.getUint8(offset+0x8);
|
||||
info.height = view.getUint8(offset+0x9);
|
||||
info.nullCharIndex = view.getUint16(offset+0xA, true);
|
||||
info.unknown2 = view.getUint8(offset+0xC);
|
||||
info.width = view.getUint8(offset+0xD);
|
||||
info.widthBis = view.getUint8(offset+0xE);
|
||||
info.encoding = view.getUint8(offset+0xF);
|
||||
|
||||
info.offsetCGLP = view.getUint32(offset+0x10, true); //character graphics
|
||||
info.offsetCWDH = view.getUint32(offset+0x14, true); //character width
|
||||
info.offsetCMAP = view.getUint32(offset+0x18, true); //character map
|
||||
|
||||
if (info.blockSize == 0x20) {
|
||||
//extra info
|
||||
info.fontHeight = view.getUint8(offset+0x1C);
|
||||
info.fontWidth = view.getUint8(offset+0x1D);
|
||||
info.bearingX = view.getUint8(offset+0x1E);
|
||||
info.bearingY = view.getUint8(offset+0x1F);
|
||||
}
|
||||
|
||||
loadCGLP(view);
|
||||
loadCWDH(view);
|
||||
loadCMAP(view);
|
||||
|
||||
mainOff = offset;
|
||||
}
|
||||
|
||||
function loadCGLP(view) {
|
||||
var offset = t.info.offsetCGLP - 8;
|
||||
|
||||
var cglp = {};
|
||||
cglp.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
cglp.blockSize = view.getUint32(offset+0x4, true);
|
||||
cglp.tileWidth = view.getUint8(offset+0x8);
|
||||
cglp.tileHeight = view.getUint8(offset+0x9);
|
||||
cglp.tileLength = view.getUint16(offset+0xA, true);
|
||||
cglp.unknown = view.getUint16(offset+0xC, true);
|
||||
cglp.depth = view.getUint8(offset+0xE);
|
||||
cglp.rotateMode = view.getUint8(offset+0xF);
|
||||
|
||||
offset += 0x10;
|
||||
cglp.tiles = [];
|
||||
var total = (cglp.blockSize - 0x10) / cglp.tileLength;
|
||||
for (var i=0; i<total; i++) {
|
||||
cglp.tiles.push(new Uint8Array(view.buffer.slice(offset, offset+cglp.tileLength)));
|
||||
offset += cglp.tileLength;
|
||||
}
|
||||
t.cglp = cglp;
|
||||
}
|
||||
|
||||
function loadCWDH(view) {
|
||||
var offset = t.info.offsetCWDH - 8;
|
||||
|
||||
var cwdh = {};
|
||||
cwdh.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
cwdh.blockSize = view.getUint32(offset+0x4, true);
|
||||
cwdh.firstCode = view.getUint16(offset+0x8, true);
|
||||
cwdh.lastCode = view.getUint16(offset+0xA, true);
|
||||
|
||||
cwdh.unknown = view.getUint32(offset+0xC, true);
|
||||
|
||||
cwdh.info = [];
|
||||
offset += 0x10;
|
||||
for (var i=0; i<t.cglp.tiles.length; i++) {
|
||||
var info = {};
|
||||
info.pixelStart = view.getInt8(offset);
|
||||
info.pixelWidth = view.getUint8(offset+1);
|
||||
info.pixelLength = view.getUint8(offset+2);
|
||||
cwdh.info.push(info);
|
||||
offset += 3;
|
||||
}
|
||||
|
||||
t.cwdh = cwdh;
|
||||
}
|
||||
|
||||
function loadCMAP(view) {
|
||||
var offset = t.info.offsetCMAP - 8;
|
||||
var cmaps = [];
|
||||
var charMap = {};
|
||||
while (offset > 0 && offset < view.byteLength) {
|
||||
var cmap = {};
|
||||
cmap.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||
cmap.blockSize = view.getUint32(offset+0x4, true);
|
||||
cmap.firstChar = view.getUint16(offset+0x8, true);
|
||||
cmap.lastChar = view.getUint16(offset+0xA, true);
|
||||
|
||||
cmap.typeSection = view.getUint32(offset+0xC, true);
|
||||
cmap.nextOffset = view.getUint32(offset+0x10, true);
|
||||
|
||||
offset += 0x14;
|
||||
switch (cmap.typeSection & 0xFFFF) {
|
||||
case 1: //char code list (first to last)
|
||||
cmap.charCodes = [];
|
||||
var total = (cmap.lastChar - cmap.firstChar) + 1;
|
||||
var charCode = cmap.firstChar;
|
||||
for (var i=0; i<total; i++) {
|
||||
var char = view.getUint16(offset, true);
|
||||
cmap.charCodes.push(char);
|
||||
if (char != 65535) {
|
||||
charMap[String.fromCharCode(charCode)] = char;
|
||||
}
|
||||
charCode++;
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
case 2: //char code map
|
||||
cmap.numChars = view.getUint16(offset, true);
|
||||
offset += 2;
|
||||
cmap.charMap = [];
|
||||
for (var i=0; i<cmap.numChars; i++) {
|
||||
var charCode = view.getUint16(offset, true);
|
||||
var char = view.getUint16(offset+2, true);
|
||||
cmap.charMap.push([charCode, char]);
|
||||
charMap[String.fromCharCode(charCode)] = char;
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cmap.firstCharCode = view.getUint16(offset, true);
|
||||
var total = (cmap.lastChar - cmap.firstChar) + 1;
|
||||
var charCode = cmap.firstChar;
|
||||
var char = cmap.firstCharCode;
|
||||
|
||||
for (var i=0; i<total; i++) {
|
||||
charMap[String.fromCharCode(charCode++)] = char++;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
cmaps.push(cmap);
|
||||
offset = cmap.nextOffset - 8;
|
||||
}
|
||||
|
||||
t.charMap = charMap;
|
||||
t.cmaps = cmaps;
|
||||
}
|
||||
|
||||
function readChar(view, offset) {
|
||||
return String.fromCharCode(view.getUint8(offset));
|
||||
}
|
||||
|
||||
// RENDERING FUNCTIONS
|
||||
|
||||
function mapText(text, missing) {
|
||||
if (missing == null) missing = "*";
|
||||
var map = t.charMap;
|
||||
var result = [];
|
||||
for (var i=0; i<text.length; i++) {
|
||||
var code = text[i];
|
||||
var mapped = map[code];
|
||||
if (mapped == null) mapped = map[missing];
|
||||
result.push(mapped);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function measureText(text, missing, spacing) {
|
||||
return measureMapped(mapText(text, missing), spacing);
|
||||
}
|
||||
|
||||
function measureMapped(mapped, spacing) {
|
||||
if (spacing == null) spacing = 1;
|
||||
var width = 0;
|
||||
var widths = t.cwdh.info;
|
||||
|
||||
for (var i=0; i<mapped.length; i++) {
|
||||
width += widths[mapped[i]].pixelLength + spacing; // pixelWidth is the width of drawn section - length is how wide the char is
|
||||
}
|
||||
|
||||
return [width, t.info.height];
|
||||
}
|
||||
|
||||
function drawToCanvas(text, palette, spacing) {
|
||||
if (spacing == null) spacing = 1;
|
||||
var mapped = mapText(text, "");
|
||||
var size = measureMapped(mapped, spacing);
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = size[0];
|
||||
canvas.height = size[1];
|
||||
var ctx = canvas.getContext("2d");
|
||||
|
||||
//draw chars
|
||||
var widths = t.cwdh.info;
|
||||
var position = 0;
|
||||
|
||||
for (var i=0; i<mapped.length; i++) {
|
||||
var c = mapped[i];
|
||||
var cinfo = widths[c];
|
||||
|
||||
var data = getCharData(c, palette);
|
||||
ctx.putImageData(data, position + cinfo.pixelStart, 0, 0, 0, cinfo.pixelWidth, data.height);
|
||||
|
||||
position += cinfo.pixelLength + spacing;
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function getCharData(id, pal) {
|
||||
//todo: cache?
|
||||
var cglp = t.cglp;
|
||||
var tile = cglp.tiles[id];
|
||||
var pixels = cglp.tileWidth*cglp.tileHeight;
|
||||
var d = new Uint8ClampedArray(pixels*4);
|
||||
var data = new ImageData(d, cglp.tileWidth, cglp.tileHeight);
|
||||
var depth = t.cglp.depth;
|
||||
var mask = (1<<depth)-1;
|
||||
|
||||
var bit = 8;
|
||||
var byte = 0;
|
||||
var curByte = tile[byte];
|
||||
var ind = 0;
|
||||
for (var i=0; i<pixels; i++) {
|
||||
bit -= depth;
|
||||
var pind = 0;
|
||||
if (bit < 0) {
|
||||
//overlap into next
|
||||
var end = bit + 8;
|
||||
if (end < 8) {
|
||||
//still some left in this byte
|
||||
pind = (curByte << (-bit)) & mask;
|
||||
}
|
||||
curByte = tile[++byte];
|
||||
bit += 8;
|
||||
pind |= (curByte >> (bit)) & mask;
|
||||
} else {
|
||||
pind = (curByte >> (bit)) & mask;
|
||||
}
|
||||
|
||||
var col = pal[pind];
|
||||
d[ind++] = col[0];
|
||||
d[ind++] = col[1];
|
||||
d[ind++] = col[2];
|
||||
d[ind++] = col[3];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,6 +66,7 @@ window.nitro = new function() {
|
||||
for (var j=0; j<16; j++) {
|
||||
name += readChar(view, offset++)
|
||||
}
|
||||
objectData[i].name = name;
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
|
||||
+19
-14
@@ -15,7 +15,7 @@ window.nsbca = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var animData;
|
||||
var speeds = [1.0, 0.5, 1/3];
|
||||
var speeds = [1.0, 0.5, 1/4];
|
||||
var mainObj = this;
|
||||
|
||||
if (input != null) {
|
||||
@@ -72,10 +72,10 @@ window.nsbca = function(input) {
|
||||
|
||||
function readTrans(view, off, obj) {
|
||||
var flag = view.getUint16(off, true); //--zyx-Sr-RZYX-T-
|
||||
|
||||
off += 4;
|
||||
|
||||
var transform = {};
|
||||
transform.flag = flag;
|
||||
|
||||
if (!((flag>>1) & 1)) { //T: translation
|
||||
var translate = [[], [], []]; //store translations in x,y,z arrays
|
||||
@@ -96,7 +96,7 @@ window.nsbca = function(input) {
|
||||
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 length = Math.ceil((inf.endFrame-inf.startFrame)*inf.speed);
|
||||
var w = (inf.halfSize)?2:4;
|
||||
|
||||
var off2 = obj.baseOff+inf.off;
|
||||
@@ -132,7 +132,7 @@ window.nsbca = function(input) {
|
||||
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 length = Math.ceil((inf.endFrame-inf.startFrame)*inf.speed);
|
||||
|
||||
var off2 = obj.baseOff+inf.off;
|
||||
try {
|
||||
@@ -173,7 +173,7 @@ window.nsbca = function(input) {
|
||||
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 length = Math.ceil((inf.endFrame-inf.startFrame)*inf.speed);
|
||||
var w = ((inf.halfSize)?2:4);
|
||||
|
||||
var off2 = obj.baseOff+inf.off;
|
||||
@@ -211,21 +211,26 @@ window.nsbca = function(input) {
|
||||
};
|
||||
} 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 d1 = view.getUint16(off2, true);
|
||||
var d2 = view.getUint16(off2+2, true);
|
||||
var d3 = view.getUint16(off2+4, true);
|
||||
var d4 = view.getUint16(off2+6, true);
|
||||
var d5 = view.getUint16(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;
|
||||
//if (i6&4096) i6 = (-8192)+i6;
|
||||
|
||||
var v1 = [d1>>3, d2>>3, d3>>3]
|
||||
var v2 = [d4>>3, d5>>3, i6]
|
||||
var v1 = [d1>>3, d2>>3, d3>>3];
|
||||
var v2 = [d4>>3, d5>>3, i6];
|
||||
|
||||
for (var i=0; i<3; i++) {
|
||||
if (v1[i] & 4096) v1[i] -= 8192;
|
||||
if (v2[i] & 4096) v2[i] -= 8192;
|
||||
}
|
||||
|
||||
vec3.scale(v1, v1, 1/4096);
|
||||
vec3.scale(v2, v2, 1/4096);
|
||||
var v3 = vec3.cross([], v1, v2)
|
||||
var v3 = vec3.cross([], v1, v2);
|
||||
|
||||
var mat = [
|
||||
v1[0], v1[1], v1[2],
|
||||
|
||||
+72
-15
@@ -70,6 +70,7 @@ window.nsbmd = function(input) {
|
||||
head.maxStack = view.getUint8(offset+0x1A);
|
||||
|
||||
head.scale = view.getInt32(offset+0x1C, true)/4096;
|
||||
head.downScale = view.getInt32(offset+0x20, true)/4096; //usually inverse of the above
|
||||
|
||||
head.numVerts = view.getUint16(offset+0x24, true);
|
||||
head.numSurfaces = view.getUint16(offset+0x26, true);
|
||||
@@ -93,6 +94,12 @@ window.nsbmd = function(input) {
|
||||
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);
|
||||
|
||||
//bind tex and palt names to their materials
|
||||
for (var i=0; i<materials.objectData.length; i++) {
|
||||
materials.objectData[i].texName = tex.names[materials.objectData[i].tex];
|
||||
materials.objectData[i].palName = palt.names[materials.objectData[i].pal];
|
||||
}
|
||||
|
||||
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}
|
||||
@@ -101,14 +108,18 @@ window.nsbmd = function(input) {
|
||||
function parseBones(offset, view, polys, materials, objects, maxStack) {
|
||||
var last;
|
||||
var commands = [];
|
||||
var debug = true;
|
||||
if (debug) console.log("== Begin Parse Bones ==");
|
||||
|
||||
var freeStack = maxStack;
|
||||
var forceID=null;
|
||||
var lastMat = null;
|
||||
var bound = [];
|
||||
|
||||
var matMap = [];
|
||||
|
||||
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++);
|
||||
@@ -117,21 +128,50 @@ window.nsbmd = function(input) {
|
||||
|
||||
var object = objects.objectData[obj];
|
||||
object.parent = parent;
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Multiply stack with object " + obj + " bound to parent " + parent);
|
||||
|
||||
commands.push({obj:obj, parent:parent, stackID:freeStack++});
|
||||
break;
|
||||
case 0x26:
|
||||
case 0x46: //placed in the stack at stack id
|
||||
case 0x66:
|
||||
var obj = view.getUint8(offset++);
|
||||
var parent = view.getUint8(offset++);
|
||||
var zero = view.getUint8(offset++);
|
||||
var stackID = view.getUint8(offset++);
|
||||
var restoreID = null;
|
||||
if (last == 0x66) restoreID = view.getUint8(offset++);
|
||||
if (last == 0x46) {
|
||||
restoreID = stackID;
|
||||
stackID = freeStack++;
|
||||
}
|
||||
|
||||
var object = objects.objectData[obj];
|
||||
object.parent = parent;
|
||||
|
||||
commands.push({obj:obj, parent:parent, stackID:((last == 0x26)?stackID:freeStack++), restoreID:((last == 0x46)?stackID:null)});
|
||||
if (debug) {
|
||||
var debugMessage = "[0x"+last.toString(16)+"] ";
|
||||
if (restoreID != null) debugMessage += "Restore matrix at " + stackID + ", ";
|
||||
debugMessage += "Multiply stack with object " + obj + " bound to parent " + parent;
|
||||
if (stackID != null) debugMessage += ", store in " + stackID;
|
||||
console.log(debugMessage);
|
||||
}
|
||||
var item = {obj:obj, parent:parent, stackID:stackID, restoreID:restoreID};
|
||||
if (bound[stackID]) {
|
||||
//we're updating a matrix that is already bound...
|
||||
//we must move copy the old value of the matrix to another place, and update the polys that point to it to the new location.
|
||||
//(does not play well with skinned meshes (they don't do this anyways), but fixes lots of "multiple object" meshes.)
|
||||
console.log("!! Already bound !! Moving old matrix at " + stackID + " to " + freeStack);
|
||||
var poly = polys.objectData;
|
||||
for (var i=0; i<poly.length; i++) {
|
||||
if (poly[i].stackID == stackID) poly[i].stackID = freeStack;
|
||||
}
|
||||
commands.push({copy: stackID, dest: freeStack++})
|
||||
}
|
||||
|
||||
commands.push(item);
|
||||
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++);
|
||||
@@ -142,8 +182,11 @@ window.nsbmd = function(input) {
|
||||
var object = objects.objectData[obj];
|
||||
object.parent = parent;
|
||||
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Restore matrix at " + restoreID + ", multiply stack with object " + obj + " bound to parent " + parent + ", store in " + stackID);
|
||||
|
||||
commands.push({obj:obj, parent:parent, stackID:stackID, restoreID:restoreID});
|
||||
break;
|
||||
*/
|
||||
case 0x04:
|
||||
case 0x24:
|
||||
case 0x44: //bind material to polygon: matID, 5, polyID
|
||||
@@ -151,54 +194,69 @@ window.nsbmd = function(input) {
|
||||
lastMat = mat;
|
||||
var five = view.getUint8(offset++); //???
|
||||
var poly = view.getUint8(offset++);
|
||||
polys.objectData[poly].stackID = (forceID == null)?(commands[commands.length-1].stackID):forceID;
|
||||
var bindID = (forceID == null)?(commands[commands.length-1].stackID):forceID;
|
||||
bound[bindID] = true;
|
||||
polys.objectData[poly].stackID = bindID;
|
||||
polys.objectData[poly].mat = mat;
|
||||
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Bind material " + mat + " to poly " + poly + " (with stack id " + polys.objectData[poly].stackID + ")");
|
||||
break;
|
||||
case 1:
|
||||
//end of all
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] END OF BONES");
|
||||
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 (debug) console.log("[0x"+last.toString(16)+"] Set object " + node + " visibility: " + vis);
|
||||
if (node > 10) debugger;
|
||||
break;
|
||||
case 3: //stack id for poly (wit)
|
||||
forceID = view.getUint8(offset++);
|
||||
console.log("stackid is "+forceID);
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Force stack id to " + forceID);
|
||||
case 0:
|
||||
break;
|
||||
case 5:
|
||||
//i don't... what??
|
||||
//holy shp!
|
||||
case 5: //"draw a mesh" supposedly. might require getting a snapshot of the matrices at this point
|
||||
var poly = view.getUint8(offset++);
|
||||
polys.objectData[poly].stackID = (stackID == null)?(commands[commands.length-1].forceID):forceID;
|
||||
var bindID = (forceID == null)?(commands[commands.length-1].stackID):forceID;
|
||||
bound[bindID] = true;
|
||||
polys.objectData[poly].stackID = bindID;
|
||||
polys.objectData[poly].mat = lastMat;
|
||||
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Draw " + poly + "(stack id " + polys.objectData[poly].stackID + ")");
|
||||
break;
|
||||
case 7:
|
||||
//sets object to be billboard
|
||||
var obj = view.getUint8(offset++);
|
||||
objects.objectData[obj].billboardMode = 1;
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Object " + obj + " set to full billboard mode.");
|
||||
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;
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Object " + obj + " set to Y billboard mode.");
|
||||
mainObj.hasBillboards = true;
|
||||
break;
|
||||
case 9: //skinning equ. not used?
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Skinning Equation (UNIMPLEMENTED)");
|
||||
debugger;
|
||||
break;
|
||||
case 0x0b:
|
||||
break; //begin, not quite sure what of. doesn't seem to change anything
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] BEGIN PAIRING.");
|
||||
break; //begin polygon material paring (scale up? using scale value in model..)
|
||||
case 0x2b:
|
||||
break; //end
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] END PAIRING.");
|
||||
break; //end polygon material pairing (scale down? using scale(down) value in model..)
|
||||
default:
|
||||
console.log("bone transform unknown: "+last);
|
||||
console.log("bone transform unknown: 0x"+last.toString(16));
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if (window.throwWhatever) debugger;
|
||||
|
||||
if (debug) console.log("== End Parse Bones ==");
|
||||
return commands;
|
||||
}
|
||||
|
||||
@@ -213,7 +271,6 @@ window.nsbmd = function(input) {
|
||||
}*/
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ window.nsbta = function(input) {
|
||||
if (i == 2) obj[prop[i]].push(data2);
|
||||
} else { //data is found at offset
|
||||
frames = frames>>obj.frameStep[prop[i]];
|
||||
if (obj.frameStep[prop[i]] > 0) frames++; //one extra frame, for interpolation
|
||||
//frames -= 1;
|
||||
var off = base + value-8;
|
||||
for (var j=0; j<frames*((i==2)?2:1); j++) {
|
||||
|
||||
+37
-28
@@ -38,7 +38,11 @@ window.nsbtp = function(input) {
|
||||
|
||||
var texTotal;
|
||||
var palTotal;
|
||||
var texNamesOff;
|
||||
var palNamesOff;
|
||||
|
||||
var texNames;
|
||||
var palNames;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
@@ -96,13 +100,35 @@ window.nsbtp = function(input) {
|
||||
var duration = view.getUint16(offset, true);
|
||||
texTotal = view.getUint8(offset+2);
|
||||
palTotal = view.getUint8(offset+3);
|
||||
var unknown = view.getUint16(offset+4, true);
|
||||
var unknown2 = view.getUint16(offset+6, true);
|
||||
texNamesOff = view.getUint16(offset+4, true);
|
||||
palNamesOff = view.getUint16(offset+6, true);
|
||||
|
||||
var nameOffset = matOff + texNamesOff;
|
||||
texNames = [];
|
||||
//read 16char tex names
|
||||
for (var i=0; i<texTotal; i++) {
|
||||
var name = "";
|
||||
for (var j=0; j<16; j++) {
|
||||
name += readChar(view, nameOffset++)
|
||||
}
|
||||
texNames[i] = name;
|
||||
}
|
||||
|
||||
nameOffset = matOff + palNamesOff;
|
||||
palNames = [];
|
||||
//read 16char pal names
|
||||
for (var i=0; i<palTotal; i++) {
|
||||
var name = "";
|
||||
for (var j=0; j<16; j++) {
|
||||
name += readChar(view, nameOffset++)
|
||||
}
|
||||
palNames[i] = name;
|
||||
}
|
||||
|
||||
//...then another nitro
|
||||
var data = nitro.read3dInfo(view, offset+8, matInfoHandler);
|
||||
|
||||
return {data: data, nextoff: data.nextoff, texTotal:texTotal, palTotal:palTotal, unknown:unknown, unknown2:unknown2, duration:duration};
|
||||
return {data: data, nextoff: data.nextoff, texTotal:texTotal, palTotal:palTotal, duration:duration, texNames: texNames, palNames: palNames};
|
||||
}
|
||||
|
||||
function matInfoHandler(view, offset, base) {
|
||||
@@ -124,9 +150,9 @@ window.nsbtp = function(input) {
|
||||
// 16char texname
|
||||
//then (frame of these)
|
||||
// 16char palname
|
||||
// texture animations are bound to the material via the name of this block.
|
||||
|
||||
var frames = view.getUint16(offset, true);
|
||||
obj.matinfo = view.getUint16(offset+2, true);
|
||||
var frames = view.getUint32(offset, true);
|
||||
obj.flags = view.getUint16(offset+4, true);
|
||||
var offset2 = view.getUint16(offset+6, true);
|
||||
offset += 8;
|
||||
@@ -135,33 +161,16 @@ window.nsbtp = function(input) {
|
||||
offset = matOff + offset2;
|
||||
//info and timing for each frame
|
||||
for (var i=0; i<frames; i++) {
|
||||
obj.frames[i] = {
|
||||
var entry = {
|
||||
time: view.getUint16(offset, true),
|
||||
tex: view.getUint8(offset+2),
|
||||
mat: view.getUint8(offset+3),
|
||||
tex: view.getUint8(offset+2), //index into names?
|
||||
pal: view.getUint8(offset+3), //index into pal names?
|
||||
}
|
||||
entry.texName = texNames[entry.tex];
|
||||
entry.palName = palNames[entry.pal];
|
||||
obj.frames.push(entry);
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
obj.texNames = [];
|
||||
//read 16char tex names
|
||||
for (var i=0; i<texTotal; i++) {
|
||||
var name = "";
|
||||
for (var j=0; j<16; j++) {
|
||||
name += readChar(view, offset++)
|
||||
}
|
||||
obj.texNames[i] = name;
|
||||
}
|
||||
|
||||
obj.palNames = [];
|
||||
//read 16char pal names
|
||||
for (var i=0; i<palTotal; i++) {
|
||||
var name = "";
|
||||
for (var j=0; j<16; j++) {
|
||||
name += readChar(view, offset++)
|
||||
}
|
||||
obj.palNames[i] = name;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,21 @@ window.nsbtx = function(input, tex0) {
|
||||
paletteInfo = nitro.read3dInfo(view, mainOff + palInfoOff, palInfoHandler);
|
||||
textureInfo = nitro.read3dInfo(view, mainOff + texInfoOff, texInfoHandler);
|
||||
|
||||
buildNameToIndex(paletteInfo);
|
||||
buildNameToIndex(textureInfo);
|
||||
|
||||
thisObj.paletteInfo = paletteInfo;
|
||||
thisObj.textureInfo = textureInfo;
|
||||
}
|
||||
|
||||
function buildNameToIndex(info) {
|
||||
var nameToIndex = {};
|
||||
for (var i=0; i<info.names.length; i++) {
|
||||
nameToIndex["$" + info.names[i]] = i;
|
||||
}
|
||||
info.nameToIndex = nameToIndex;
|
||||
}
|
||||
|
||||
function readTexWithPal(textureId, palId) {
|
||||
var tex = textureInfo.objectData[textureId];
|
||||
var pal = paletteInfo.objectData[palId];
|
||||
@@ -106,6 +117,7 @@ window.nsbtx = function(input, tex0) {
|
||||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&31, trans);
|
||||
col[3] = (dat>>5)*(255/7);
|
||||
premultiply(col);
|
||||
|
||||
} else if (format == 2) { //2 bit pal
|
||||
if (i%4 == 0) databuf = texView.getUint8(off++);
|
||||
@@ -126,6 +138,7 @@ window.nsbtx = function(input, tex0) {
|
||||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&7, trans);
|
||||
col[3] = (dat>>3)*(255/31);
|
||||
premultiply(col);
|
||||
|
||||
} else if (format == 7) { //raw color data
|
||||
col = texView.getUint16(off, true);
|
||||
@@ -134,6 +147,7 @@ window.nsbtx = function(input, tex0) {
|
||||
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
||||
colourBuffer[3] = Math.round((col>>15)*255);
|
||||
col = colourBuffer;
|
||||
premultiply(col);
|
||||
off += 2;
|
||||
|
||||
} else {
|
||||
@@ -146,6 +160,12 @@ window.nsbtx = function(input, tex0) {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function premultiply(col) {
|
||||
col[0] *= col[3]/255;
|
||||
col[1] *= col[3]/255;
|
||||
col[2] *= col[3]/255;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
+556
-547
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user