/// <summary> /// Load materials in stream. /// </summary> /// <param name="stream">Stream to use.</param> /// <returns>Material definitions.</returns> public static IEnumerable<NsbmdMaterial> ReadTex0(Stream stream, int blockoffset, out int ptexnum, out int ppalnum, out List<NSBMDTexture> texs, out List<NSBMDPalette> pals) { EndianBinaryReader reader = new EndianBinaryReader(stream, Endianness.LittleEndian); UInt32 blocksize, blockptr, blocklimit; int texnum; UInt32 texdataoffset; int texdatasize; UInt32 sptexoffset; // for 4x4 compressed texels only int sptexsize; // for 4x4 compressed texels only UInt32 spdataoffset; // for 4x4 compressed texels only int palnum; UInt32 paldefoffset; UInt32 paldataoffset; int paldatasize; NsbmdMaterial[] material = null; int i, j; texs = new List<NSBMDTexture>(); pals = new List<NSBMDPalette>(); blockptr = (uint)(blockoffset + 4); // already read the block ID, so skip 4 bytes blocksize = reader.ReadUInt32(); // block size blocklimit = (uint)(blocksize + blockoffset); Console.WriteLine("DEBUG: blockoffset = {0}, blocksize = {1}", blockoffset, blocksize); stream.Skip(4); // skip 4 padding 0s texdatasize = reader.ReadUInt16() << 3; // total texture data size div8 stream.Skip(6); // skip 6 bytes texdataoffset = (uint)(reader.ReadUInt32() + blockoffset); stream.Skip(4); // skip 4 padding 0s sptexsize = reader.ReadUInt16() << 3; // for format 5 4x4-texel, data size div8 stream.Skip(6); // skip 6 bytesmhm sptexoffset = (uint)(reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, data offset spdataoffset = (uint)(reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, palette info stream.Skip(4); // skip 4 bytes paldatasize = reader.ReadUInt16() << 3; // total palette data size div8 stream.Skip(2); // skip 2 bytes paldefoffset = (uint)(reader.ReadUInt32() + blockoffset); paldataoffset = (uint)(reader.ReadUInt32() + blockoffset); // printf( "texdataoffset = %08x texdatasize = %08x\n", texdataoffset, texdatasize ); // printf( "sptexoffset = %08x sptexsize = %08x spdataoffset = %08x\n", sptexoffset, sptexsize, spdataoffset ); // printf( "paldataoffset = %08x paldatasize = %08x\n", paldataoffset, paldatasize ); //////////////////////////////////////////////// // texture definition stream.Skip(1); // skip dummy '0' texnum = reader.ReadByte(); // no of texture blockptr = (uint)stream.Position; stream.Seek(paldefoffset, SeekOrigin.Begin); stream.Skip(1); // skip dummy '0' palnum = reader.ReadByte(); // no of palette stream.Seek(blockptr, SeekOrigin.Begin); Console.WriteLine("texnum = {0}, palnum = {1}", texnum, palnum); // allocate memory for material, great enough to hold all texture and palette material = new NsbmdMaterial[(texnum > palnum ? texnum : palnum)]; for (i = 0; i < material.Length; i++) material[i] = new NsbmdMaterial(); stream.Skip(14 + (texnum * 4)); // go straight to texture info for (i = 0; i < texnum; i++) { UInt32 offset; int param; int format; int width; int height; var mat = material[i]; offset = (uint)(reader.ReadUInt16() << 3); param = reader.ReadUInt16(); // texture parameter stream.Skip(4); // skip 4 bytes format = (param >> 10) & 7; // format 0..7, see DSTek width = 8 << ((param >> 4) & 7); height = 8 << ((param >> 7) & 7); mat.color0 = (param >> 13) & 1; if (format == 5) mat.texoffset = offset + sptexoffset; // 4x4-Texel Compressed Texture else mat.texoffset = offset + texdataoffset; mat.format = format; mat.width = width; mat.height = height; NSBMDTexture t = new NSBMDTexture(); t.format = format; t.width = width; t.height = height; t.color0 = (param >> 13) & 1; texs.Add(t); } //////////////////////////////////////////// // copy texture names for (i = 0; i < texnum; i++) { material[i].texname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; texs[i].texname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each texture's size for (i = 0; i < texnum; i++) { int[] bpp = { 0, 8, 2, 4, 8, 2, 8, 16 }; var mat = material[i]; mat.texsize = (uint)(mat.width * mat.height * bpp[mat.format] / 8); Console.WriteLine("tex {0} '{1}': offset = {2} size = {3} [W,H] = [{4}, {5}]", i, mat.texname, mat.texoffset, mat.texsize, mat.width, mat.height); texs[i].texsize = (uint)(mat.width * mat.height * bpp[mat.format] / 8); } //////////////////////////////////////////////// // palette definition stream.Seek(paldefoffset + 2, SeekOrigin.Begin); // skip palnum, already read stream.Seek(14 + (palnum * 4), SeekOrigin.Current); // go straight to palette info for (i = 0; i < palnum; i++) { uint curOffset = (uint)((reader.ReadUInt16() << 3) + paldataoffset); stream.Seek(2, SeekOrigin.Current); // skip 2 bytes material[i].paloffset = curOffset; NSBMDPalette t = new NSBMDPalette(); t.paloffset = curOffset; pals.Add(t); } //////////////////////////////////////////////// // copy palette names for (i = 0; i < palnum; i++) { var mat = material[i]; mat.palname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; pals[i].palname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each palette's size // assume the palettes are stored sequentially /*for (i = 0; i < palnum - 1; i++) { int r; var mat = material[i]; r = i; try { while (material[r].paloffset == mat.paloffset) r++; } catch { } // below is RotA stupid way to calculate the size of palette: next's offset - current's offset // it works most of the time if (r != palnum) { mat.palsize = material[r].paloffset - mat.paloffset; pals[i].palsize = material[r].paloffset - mat.paloffset; } else { mat.palsize = blocklimit - mat.paloffset; pals[i].palsize = blocklimit - mat.paloffset; } //printf("pal '%s' size = %d\n", mat->palname, mat->palsize); } material[i].palsize = blocklimit - material[i].paloffset; pals[i].palsize = blocklimit - material[i].paloffset;*/ List<int> offsets = new List<int>(); for (int k = 0; k < pals.Count; k++) { if (!offsets.Contains((int)pals[k].paloffset)) { offsets.Add((int)pals[k].paloffset); } } offsets.Add((int)blocklimit); offsets.Sort(); for (int k = 0; k < pals.Count; k++) { int pallength; int l = -1; do { l++; } while (offsets[l] - pals[k].paloffset <= 0);//nsbtx.PalInfo.infoBlock.PalInfo[i + j].Palette_Offset - nsbtx.PalInfo.infoBlock.PalInfo[i].Palette_Offset == 0) pallength = offsets[l] - (int)pals[k].paloffset; //RGBA[] c_ = pals[k].paldata; //List<RGBA> c = new List<RGBA>(); //c.AddRange(pals[k].paldata.Take(pallength / 2)); //pals[k].paldata = c.ToArray(); pals[k].palsize = (uint)pallength; material[k].palsize = (uint)pallength; } //////////////////////////////////////////////// // traverse each texture for (i = 0; i < texnum; i++) { var mat = material[i]; stream.Seek(mat.texoffset, SeekOrigin.Begin); //////////////////////////////////////////////// // read texture into memory byte[] by = reader.ReadBytes((int)mat.texsize); mat.texdata = by; texs[i].texdata = by; Console.WriteLine("DEBUG: texoffset = {0}, texsize = {1}", mat.texoffset, mat.texsize); //////////////////////////////////////////////// // additional data for format 5 4x4 compressed texels if (mat.format == 5) { UInt32 r = mat.texsize / 2;//>> 1; stream.Seek(spdataoffset + (mat.texoffset - sptexoffset) / 2, SeekOrigin.Begin); by = reader.ReadBytes((int)r); mat.spdata = by; texs[i].spdata = by; Console.WriteLine("DEBUG: 4x4-texel spdataoffset = {0}, spdatasize = {1}", spdataoffset, r); //spdataoffset += r; } } //////////////////////////////////////////////// // traverse each palette for (i = 0; i < palnum; i++) { try { NsbmdMaterial mat = material[i]; var palentry = mat.palsize >> 1; RGBA[] rgbq = new RGBA[palentry]; Console.WriteLine("DEBUG: converting pal '{0}', palentry = {1}", mat.palname, palentry); stream.Seek(mat.paloffset, SeekOrigin.Begin); for (j = 0; j < palentry; j++) { UInt16 p = reader.ReadUInt16(); rgbq[j].R = (byte)(((p >> 0) & 0x1f) << 3); // red rgbq[j].G = (byte)(((p >> 5) & 0x1f) << 3); // green rgbq[j].B = (byte)(((p >> 10) & 0x1f) << 3); // blue //rgbq[j].RotA = (p&0x8000) ? 0xff : 0; rgbq[j].A = (p & 0x8000) == 0 ? (byte)0xff : (byte)0;//0xff; // alpha } mat.paldata = rgbq; pals[i].paldata = rgbq; } catch { } } ptexnum = texnum; ppalnum = palnum; return material; }
/// <summary> /// Load materials in stream. /// </summary> /// <param name="stream">Stream to use.</param> /// <returns>Material definitions.</returns> public static IEnumerable <NSBMDMaterial> ReadTex0(Stream stream, int blockoffset, out int ptexnum, out int ppalnum, out List <NSBMDTexture> texs, out List <NSBMDPalette> pals) { EndianBinaryReader reader = new EndianBinaryReader(stream, Endianness.LittleEndian); UInt32 blocksize, blockptr, blocklimit; int texnum; UInt32 texdataoffset; int texdatasize; UInt32 sptexoffset; // for 4x4 compressed texels only int sptexsize; // for 4x4 compressed texels only UInt32 spdataoffset; // for 4x4 compressed texels only int palnum; UInt32 paldefoffset; UInt32 paldataoffset; int paldatasize; NSBMDMaterial[] material = null; int i, j; texs = new List <NSBMDTexture>(); pals = new List <NSBMDPalette>(); blockptr = (uint)(blockoffset + 4); // already read the block ID, so skip 4 bytes blocksize = reader.ReadUInt32(); // block size blocklimit = (uint)(blocksize + blockoffset); Console.WriteLine("DEBUG: blockoffset = {0}, blocksize = {1}", blockoffset, blocksize); stream.Skip(4); // skip 4 padding 0s texdatasize = reader.ReadUInt16() << 3; // total texture data size div8 stream.Skip(6); // skip 6 bytes texdataoffset = (uint)(reader.ReadUInt32() + blockoffset); stream.Skip(4); // skip 4 padding 0s sptexsize = reader.ReadUInt16() << 3; // for format 5 4x4-texel, data size div8 stream.Skip(6); // skip 6 bytesmhm sptexoffset = (uint)(reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, data offset spdataoffset = (uint)(reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, palette info stream.Skip(4); // skip 4 bytes paldatasize = reader.ReadUInt16() << 3; // total palette data size div8 stream.Skip(2); // skip 2 bytes paldefoffset = (uint)(reader.ReadUInt32() + blockoffset); paldataoffset = (uint)(reader.ReadUInt32() + blockoffset); // printf( "texdataoffset = %08x texdatasize = %08x\n", texdataoffset, texdatasize ); // printf( "sptexoffset = %08x sptexsize = %08x spdataoffset = %08x\n", sptexoffset, sptexsize, spdataoffset ); // printf( "paldataoffset = %08x paldatasize = %08x\n", paldataoffset, paldatasize ); //////////////////////////////////////////////// // texture definition stream.Skip(1); // skip dummy '0' texnum = reader.ReadByte(); // no of texture blockptr = (uint)stream.Position; stream.Seek(paldefoffset, SeekOrigin.Begin); stream.Skip(1); // skip dummy '0' palnum = reader.ReadByte(); // no of palette stream.Seek(blockptr, SeekOrigin.Begin); Console.WriteLine("texnum = {0}, palnum = {1}", texnum, palnum); // allocate memory for material, great enough to hold all texture and palette material = new NSBMDMaterial[(texnum > palnum ? texnum : palnum)]; for (i = 0; i < material.Length; i++) { material[i] = new NSBMDMaterial(); } stream.Skip(14 + (texnum * 4)); // go straight to texture info for (i = 0; i < texnum; i++) { UInt32 offset; int param; int format; int width; int height; var mat = material[i]; offset = (uint)(reader.ReadUInt16() << 3); param = reader.ReadUInt16(); // texture parameter stream.Skip(4); // skip 4 bytes format = (param >> 10) & 7; // format 0..7, see DSTek width = 8 << ((param >> 4) & 7); height = 8 << ((param >> 7) & 7); mat.color0 = (param >> 13) & 1; if (format == 5) { mat.texoffset = offset + sptexoffset; // 4x4-Texel Compressed Texture } else { mat.texoffset = offset + texdataoffset; } mat.format = format; mat.width = width; mat.height = height; NSBMDTexture t = new NSBMDTexture(); t.format = format; t.width = width; t.height = height; t.color0 = (param >> 13) & 1; texs.Add(t); } //////////////////////////////////////////// // copy texture names for (i = 0; i < texnum; i++) { material[i].texname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; texs[i].texname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each texture's size for (i = 0; i < texnum; i++) { int[] bpp = { 0, 8, 2, 4, 8, 2, 8, 16 }; var mat = material[i]; mat.texsize = (uint)(mat.width * mat.height * bpp[mat.format] / 8); Console.WriteLine("tex {0} '{1}': offset = {2} size = {3} [W,H] = [{4}, {5}]", i, mat.texname, mat.texoffset, mat.texsize, mat.width, mat.height); texs[i].texsize = (uint)(mat.width * mat.height * bpp[mat.format] / 8); } //////////////////////////////////////////////// // palette definition stream.Seek(paldefoffset + 2, SeekOrigin.Begin); // skip palnum, already read stream.Seek(14 + (palnum * 4), SeekOrigin.Current); // go straight to palette info for (i = 0; i < palnum; i++) { uint curOffset = (uint)((reader.ReadUInt16() << 3) + paldataoffset); stream.Seek(2, SeekOrigin.Current); // skip 2 bytes material[i].paloffset = curOffset; NSBMDPalette t = new NSBMDPalette(); t.paloffset = curOffset; pals.Add(t); } //////////////////////////////////////////////// // copy palette names for (i = 0; i < palnum; i++) { var mat = material[i]; mat.palname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; pals[i].palname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each palette's size // assume the palettes are stored sequentially /*for (i = 0; i < palnum - 1; i++) * { * int r; * var mat = material[i]; * r = i; * try { while (material[r].paloffset == mat.paloffset) r++; } * catch { } * // below is RotA stupid way to calculate the size of palette: next's offset - current's offset * // it works most of the time * if (r != palnum) * { * mat.palsize = material[r].paloffset - mat.paloffset; * pals[i].palsize = material[r].paloffset - mat.paloffset; * } * else * { * mat.palsize = blocklimit - mat.paloffset; * pals[i].palsize = blocklimit - mat.paloffset; * } * //printf("pal '%s' size = %d\n", mat->palname, mat->palsize); * } * material[i].palsize = blocklimit - material[i].paloffset; * pals[i].palsize = blocklimit - material[i].paloffset;*/ List <int> offsets = new List <int>(); for (int k = 0; k < pals.Count; k++) { if (!offsets.Contains((int)pals[k].paloffset)) { offsets.Add((int)pals[k].paloffset); } } offsets.Add((int)blocklimit); offsets.Sort(); for (int k = 0; k < pals.Count; k++) { int pallength; int l = -1; do { l++; }while (offsets[l] - pals[k].paloffset <= 0);//nsbtx.PalInfo.infoBlock.PalInfo[i + j].Palette_Offset - nsbtx.PalInfo.infoBlock.PalInfo[i].Palette_Offset == 0) pallength = offsets[l] - (int)pals[k].paloffset; //RGBA[] c_ = pals[k].paldata; //List<RGBA> c = new List<RGBA>(); //c.AddRange(pals[k].paldata.Take(pallength / 2)); //pals[k].paldata = c.ToArray(); pals[k].palsize = (uint)pallength; material[k].palsize = (uint)pallength; } //////////////////////////////////////////////// // traverse each texture for (i = 0; i < texnum; i++) { var mat = material[i]; stream.Seek(mat.texoffset, SeekOrigin.Begin); //////////////////////////////////////////////// // read texture into memory byte[] by = reader.ReadBytes((int)mat.texsize); mat.texdata = by; texs[i].texdata = by; Console.WriteLine("DEBUG: texoffset = {0}, texsize = {1}", mat.texoffset, mat.texsize); //////////////////////////////////////////////// // additional data for format 5 4x4 compressed texels if (mat.format == 5) { UInt32 r = mat.texsize / 2;//>> 1; stream.Seek(spdataoffset + (mat.texoffset - sptexoffset) / 2, SeekOrigin.Begin); by = reader.ReadBytes((int)r); mat.spdata = by; texs[i].spdata = by; Console.WriteLine("DEBUG: 4x4-texel spdataoffset = {0}, spdatasize = {1}", spdataoffset, r); //spdataoffset += r; } } //////////////////////////////////////////////// // traverse each palette for (i = 0; i < palnum; i++) { try { NSBMDMaterial mat = material[i]; var palentry = mat.palsize >> 1; RGBA[] rgbq = new RGBA[palentry]; Console.WriteLine("DEBUG: converting pal '{0}', palentry = {1}", mat.palname, palentry); stream.Seek(mat.paloffset, SeekOrigin.Begin); for (j = 0; j < palentry; j++) { UInt16 p = reader.ReadUInt16(); rgbq[j].R = (byte)(((p >> 0) & 0x1f) << 3); // red rgbq[j].G = (byte)(((p >> 5) & 0x1f) << 3); // green rgbq[j].B = (byte)(((p >> 10) & 0x1f) << 3); // blue //rgbq[j].RotA = (p&0x8000) ? 0xff : 0; rgbq[j].A = (p & 0x8000) == 0 ? (byte)0xff : (byte)0; //0xff; // alpha } mat.paldata = rgbq; pals[i].paldata = rgbq; } catch { } } ptexnum = texnum; ppalnum = palnum; return(material); }
/// <summary> /// ReadMld0. /// </summary> private static NsbmdModel[] ReadMdl0(Stream stream, int blockoffset) { var reader = new EndianBinaryReader(stream, Endianness.LittleEndian); int blocksize; int blockptr; int blocklimit; byte num; uint r; List<NsbmdModel> model = new List<NsbmdModel>(); //////////////////////////////////////////////// // model blockptr = blockoffset + 4; // already read the ID, skip 4 bytes blocksize = reader.ReadInt32(); // block size blocklimit = blocksize + blockoffset; stream.Skip(1); // skip dummy '0' num = reader.ReadByte(); // no of model if (num <= 0) throw new Exception(); for (var i = 0; i < num; ++i) model.Add(new NsbmdModel()); var modelOffset = new UInt32[num]; stream.Skip(10 + 4 + (num * 4)); // skip [char xyz], useless, go straight to model data offset blockptr += 10 + 4; //////////////////////////////////////////////// // copy model dataoffset for (var i = 0; i < num; i++) modelOffset[i] = (uint)(reader.ReadUInt32() + blockoffset); //////////////////////////////////////////////// // copy model names for (var i = 0; i < num; i++) model[i].Name = Utils.ReadNSBMDString(reader); //////////////////////////////////////////////// // parse model data uint totalsize_base = reader.ReadUInt32(); uint codeoffset_base = reader.ReadUInt32(); uint texpaloffset_base = reader.ReadUInt32(); uint polyoffset_base = reader.ReadUInt32(); uint polyend_base = reader.ReadUInt32(); stream.Skip(4); uint matnum = reader.ReadByte(); // no. of material uint polynum = reader.ReadByte(); // no. of polygon byte laststack = reader.ReadByte(); byte unknown1m = reader.ReadByte(); float modelscale = (float)reader.ReadInt32() / 4096f; float boundscale = (float)reader.ReadInt32();// / 4096f; int vertexcount = reader.ReadInt16(); int surfacecount = reader.ReadInt16(); int trianglecount = reader.ReadInt16(); int quadcount = reader.ReadInt16(); model[0].laststackid = laststack; model[0].modelScale = modelscale; model[0].boundScale = boundscale; model[0].boundXmin = (float)NsbmdGlRenderer.sign(reader.ReadInt16(), 16) / 4096f; model[0].boundYmin = (float)NsbmdGlRenderer.sign(reader.ReadInt16(), 16) / 4096f; model[0].boundZmin = (float)NsbmdGlRenderer.sign(reader.ReadInt16(), 16) / 4096f; model[0].boundXmax = (float)NsbmdGlRenderer.sign(reader.ReadInt16(), 16) / 4096f; model[0].boundYmax = (float)NsbmdGlRenderer.sign(reader.ReadInt16(), 16) / 4096f; model[0].boundZmax = (float)NsbmdGlRenderer.sign(reader.ReadInt16(), 16) / 4096f; var polyOffsets = new UInt32[polynum]; var polyDataSize = new UInt32[polynum]; for (var i = 0; i < 1; i++) { var mod = model[i]; stream.Seek(modelOffset[i], SeekOrigin.Begin); // the following variables are all offset values long totalsize; uint codeoffset; UInt32 texpaloffset; UInt32 polyoffset; long polyend; long texoffset; long paloffset; uint modoffset = modelOffset[i]; totalsize = totalsize_base + modoffset; codeoffset = codeoffset_base + modoffset; // additional model data, bone definition etc., just follow NsbmdObject section texpaloffset = texpaloffset_base + modoffset; polyoffset = polyoffset_base + modoffset; polyend = polyend_base + modoffset; stream.Skip(5 * 4 + 4 + 2 + 38); // go straight to NsbmdObject //////////////////////////////////////////////// // NsbmdObject section UInt32 objnum; int objdatabase; UInt32[] objdataoffset; UInt32[] objdatasize; objdatabase = (int)stream.Position; stream.Skip(1); // skip dummy '0' objnum = reader.ReadByte(); // no of NsbmdObject stream.Skip(14 + (objnum * 4)); // skip bytes, go striaght to NsbmdObject data offset for (i = 0; i < objnum; ++i) mod.Objects.Add(new NsbmdObject()); objdataoffset = new UInt32[objnum]; objdatasize = new UInt32[objnum]; for (var j = 0; j < objnum; j++) objdataoffset[j] = (UInt32)(reader.ReadUInt32() + objdatabase); for (var j = 0; j < objnum - 1; j++) objdatasize[j] = objdataoffset[j + 1] - objdataoffset[j]; objdatasize[objnum - 1] = (UInt32)(codeoffset - objdataoffset[objnum - 1]); //////////////////////////////////////////////// // copy NsbmdObject names for (var j = 0; j < objnum; j++) { mod.Objects[j].Name = Utils.ReadNSBMDString(reader); // TO DEBUG Console.WriteLine(mod.Objects[j].Name); } //////////////////////////////////////////////// // parse NsbmdObject information for (var j = 0; j < objnum; j++) { if (objdatasize[j] <= 4) continue; stream.Seek(objdataoffset[j], SeekOrigin.Begin); ParseNsbmdObject(reader, mod.Objects[j], modelscale); } //////////////////////////////////////////////// // material section stream.Seek(texpaloffset, SeekOrigin.Begin); // now get the texture and palette offset texoffset = reader.ReadUInt16() + texpaloffset; paloffset = reader.ReadUInt16() + texpaloffset; // allocate memory for material for (var j = 0; j <= matnum; j++)//i <= matnum; ++i) mod.Materials.Add(new NsbmdMaterial()); //////////////////////////////////////////////// // parse material definition // defines RotA material by pairing texture and palette stream.Seek(16 + (matnum * 4), SeekOrigin.Current); // go straight to material data offset for (var j = 0; j < matnum; j++) // TODO: BAD! { mod.Materials[j] = new NsbmdMaterial(); blockptr = (int)stream.Position; r = reader.ReadUInt32() + texpaloffset/* + 4 + 12*/;// skip 18 bytes (+ 2 bytes for texoffset, 2 bytes for paloffset) stream.Seek(r, SeekOrigin.Begin); //mod.Materials[j].repeat = reader.ReadByte(); //reader.BaseStream.Position -= 1; int dummy = reader.ReadInt16(); int sectionSize = reader.ReadInt16(); int unknown1 = reader.ReadInt32();//DifAmbColors int unknown2 = reader.ReadInt32();//SpeEmiColors int unknown3 = reader.ReadInt32();//PolyAttrib int constant2 = reader.ReadInt32();//PolyAttrib Mask int texVramOffset = reader.ReadInt16(); int texImageParam = reader.ReadInt16(); int constant3 = reader.ReadInt32();//texImageParam Mask int constant4 = reader.ReadInt32(); int matWidth = reader.ReadInt16(); int matHeight = reader.ReadInt16(); int unknown4 = reader.ReadInt16(); int unknown5 = reader.ReadInt16(); int unknown6 = reader.ReadInt32(); //int unknown7 = reader.ReadInt32();//if srt S Scale //int unknown8 = reader.ReadInt32();//if srt T Scale //int unknown9 = reader.ReadInt16();//if srt & 60 Rot //int unknownA = reader.ReadInt16();//if srt & 60 S Trans //int unknownB = reader.ReadInt16();//if srt & 60 T Trans //uint polyParam = reader.ReadUInt32(); //reader.ReadInt16(); //ushort texImageParam = reader.ReadUInt16(); mod.Materials[j].repeatS = texImageParam & 1; mod.Materials[j].repeatT = texImageParam >> 1 & 1; mod.Materials[j].flipS = texImageParam >> 2 & 1; mod.Materials[j].flipT = texImageParam >> 3 & 1; /*if ((texImageParam >> 14 & 0x03) == 1) { mod.Materials[j].scaleS = /*1 << /(texImageParam >> 12 & 2) + 1; mod.Materials[j].scaleT = /*1 << /(texImageParam >> 14 & 2) + 1; } else { mod.Materials[j].scaleS = 1; mod.Materials[j].scaleT = 1; }*/ switch (texImageParam >> 14 & 0x03) { case 0: mod.Materials[j].scaleS = 1; mod.Materials[j].scaleT = 1; mod.Materials[j].transS = 0; mod.Materials[j].transT = 0; break; case 1: { int sscale = (int)reader.ReadInt32();// >> 0 & 0xFFFFFFFF; sscale = NsbmdGlRenderer.sign(sscale, 32); int tscale = (int)reader.ReadInt32();// >> 0 & 0xFFFFFFFF; tscale = NsbmdGlRenderer.sign(tscale, 32); //int strans = (int)unknown2 >> 0 & 0xFFFF; //int ttrans = (int)unknown2 >> 16 & 0xFFFF; mod.Materials[j].scaleS = (float)sscale / 4096f; mod.Materials[j].scaleT = (float)tscale / 4096f; if (sectionSize >= 60) { mod.Materials[j].rot = (float)reader.ReadInt16() / 4096f; mod.Materials[j].transS = (float)reader.ReadInt16() / 4096f; mod.Materials[j].transT = (float)reader.ReadInt16() / 4096f; } else { } break; } case 2: case 3: mod.Materials[j].mtx = new float[16]; for (int k = 0; k < 16; k++) { mod.Materials[j].mtx[k] = reader.ReadInt32(); } break; default: break; // throw new Exception(String.Format("BMD: unsupported texture coord transform mode {0}", matgroup.m_TexParams >> 30)); } mod.Materials[j].width = matWidth; mod.Materials[j].height = matHeight; int width = 8 << (texImageParam >> 4 & 7); int height = 8 << (texImageParam >> 7 & 7); //int m_DifAmbColors = reader.ReadInt32(); //int m_SpeEmiColors = reader.ReadInt32(); mod.Materials[j].DiffuseColor = SM64DSe.Helper.BGR15ToColor((ushort)(unknown1 & 0x7FFF)); mod.Materials[j].AmbientColor = SM64DSe.Helper.BGR15ToColor((ushort)(unknown1 >> 16 & 0x7FFF)); mod.Materials[j].SpecularColor = SM64DSe.Helper.BGR15ToColor((ushort)(unknown2 & 0x7FFF)); mod.Materials[j].EmissionColor = SM64DSe.Helper.BGR15ToColor((ushort)(unknown2 >> 16 & 0x7FFF)); int a = (int)((unknown3 >> 16) & 31); mod.Materials[j].Alpha = a;//a * 2 + 1;//a * 2 + (a + 31) / 32; mod.Materials[j].PolyAttrib = (uint)unknown3; mod.Materials[j].diffuseColor = (unknown1 >> 15 & 1) == 1; mod.Materials[j].shine = (unknown2 >> 15 & 1) == 1; stream.Seek(blockptr + 4, SeekOrigin.Begin); } for (var j = 0; j < matnum; j++) { mod.Materials[j].MaterialName = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // now go to read the texture definition stream.Seek(texoffset, SeekOrigin.Begin); stream.Skip(1); // skip dummy '0' int texnum = reader.ReadByte(); Debug.Assert(texnum <= matnum); Console.WriteLine(String.Format("texnum: {0}", texnum)); if (texnum > 0) { stream.Seek(14 + (texnum * 4), SeekOrigin.Current); // go straight to data offsets for (var j = 0; j < texnum; j++) { Int32 flags = reader.ReadInt32(); int numPairs = flags >> 16 & 0xf; int dummy = flags >> 24 & 0xf; blockptr = (int)stream.Position; stream.Seek((flags & 0xffff) + texpaloffset, SeekOrigin.Begin); NSBMDTexture t = new NSBMDTexture(); for (int k = 0; k < numPairs; k++) { uint texmatid = reader.ReadByte(); mod.tex_mat.Add((int)texmatid); mod.Materials[j].texmatid.Add(texmatid); t.texmatid.Add(texmatid); } mod.Textures.Add(t); stream.Seek(blockptr, SeekOrigin.Begin); } for (var j = 0; j < texnum; j++) // copy texture names { NsbmdMaterial mat = mod.Materials[j]; mat.texname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; mod.Textures[j].texname = Utils.ReadNSBMDString(reader); Console.WriteLine("tex (matid={0}): {1}", mat.texmatid, mat.texname); } } //////////////////////////////////////////////// // now go to read the palette definition stream.Seek(paloffset, SeekOrigin.Begin); stream.Skip(1); // skip dummy '0' int palnum = reader.ReadByte(); // no of palette definition Debug.Assert(palnum <= matnum); // may not always hold? Console.WriteLine("DEBUG: palnum = {0}", palnum); if (palnum > 0) { stream.Seek(14 + (palnum * 4), SeekOrigin.Current); // go straight to data offsets for (var j = 0; j < palnum; j++) // matching palette with material { Int32 flags = reader.ReadInt32(); int numPairs = flags >> 16 & 0xf; int dummy = flags >> 24 & 0xf; blockptr = (int)stream.Position; stream.Seek((flags & 0xffff) + texpaloffset, SeekOrigin.Begin); NSBMDPalette t = new NSBMDPalette(); for (int k = 0; k < numPairs; k++) { uint texmatid = reader.ReadByte(); mod.tex_mat.Add((int)texmatid); mod.Materials[j].texmatid.Add(texmatid); t.palmatid.Add(texmatid); } mod.Palettes.Add(t); stream.Seek(blockptr, SeekOrigin.Begin); } for (var j = 0; j < palnum; j++) // copy palette names { int palmatid = (int)mod.Materials[j].palmatid; mod.Materials[palmatid].palname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; mod.Palettes[j].palname = Utils.ReadNSBMDString(reader); // TO DEBUG Console.WriteLine("pal (matid={0}): {1}", palmatid, mod.Materials[palmatid].palname); } } //////////////////////////////////////////////// // Polygon stream.Seek(polyoffset, SeekOrigin.Begin); stream.Skip(1); // skip dummy '0' r = reader.ReadByte(); // no of polygon Console.WriteLine("DEBUG: polynum = {0}", polynum); for (var j = 0; j <= polynum; j++) { mod.Polygons.Add(new NsbmdPolygon()); } stream.Skip(14 + (polynum * 4)); // skip bytes, go straight to data offset for (var j = 0; j < polynum; j++) polyOffsets[j] = reader.ReadUInt32() + polyoffset; try { for (var j = 0; j < polynum; j++) // copy polygon names { mod.Polygons[j].Name = Utils.ReadNSBMDString(reader); Console.WriteLine(mod.Polygons[j].Name); } } catch { } //////////////////////////////////////////////// // now go to the polygon data, there is RotA 16-byte-header before geometry commands for (var j = 0; j < polynum; j++) { var poly = mod.Polygons[j]; ////////////////////////////////////////////////////////// poly.MatId = -1; // DEFAULT: indicate no associated material ////////////////////////////////////////////////////////// //stream.Seek(polyOffsets[j] + 8, SeekOrigin.Begin); // skip 8 unknown bytes short dummy = reader.ReadInt16(); short headerSize = reader.ReadInt16(); int unknown2 = reader.ReadInt32(); polyOffsets[j] += reader.ReadUInt32(); polyDataSize[j] = reader.ReadUInt32(); //printf( "poly %2d '%-16s': dataoffset: %08x datasize %08x\n", j, poly->polyname, poly->dataoffset, poly->datasize ); } //} //////////////////////////////////////////////// // read the polygon data into memory for (var j = 0; j < polynum; j++) { var poly = mod.Polygons[j]; stream.Seek(polyOffsets[j], SeekOrigin.Begin); poly.PolyData = reader.ReadBytes((int)polyDataSize[j]); } //} //////////////////////////////////////////////// // decode the additional model data DecodeCode(stream, codeoffset, texpaloffset, mod, laststack); } //modelnum = num; return model.ToArray(); }