public void Parse(BinaryReader buffer, AKAOType type, long limit = long.MaxValue) { _type = type; if (buffer.BaseStream.Length < 4) { return; } if (_type == AKAOType.UNKNOWN) { // we must try to find the AKAO type byte[] header = buffer.ReadBytes(4); // AKAO if (!CheckHeader(header)) { return; } ushort v1 = buffer.ReadUInt16(); // ID or type ushort v2 = buffer.ReadUInt16(); // File Length or empty byte v3 = buffer.ReadByte(); // Type or empty byte v4 = buffer.ReadByte(); // Type var or empty if (v2 + v3 + v4 == 0) { if (v1 == 0) { _type = AKAOType.SAMPLE; } else { if (buffer.BaseStream.Position == 10) { // v1 is the sample collection ID in this case _type = AKAOType.SOUND; } else { // E075.P have an AKAO PROG without v3 = 0xC8... _type = AKAOType.PROG; } } } else if (v3 == 0xC8) { _type = AKAOType.EFFECT; } else if (v2 > 0 && v2 == buffer.BaseStream.Length) { _type = AKAOType.MUSIC; } buffer.BaseStream.Position -= 10; } switch (_type) { case AKAOType.MUSIC: //https://github.com/vgmtrans/vgmtrans/blob/master/src/main/formats/AkaoSeq.cpp // header datas byte[] header = buffer.ReadBytes(4); // AKAO ushort fileId = buffer.ReadUInt16(); ushort byteLen = buffer.ReadUInt16(); ushort reverb = buffer.ReadUInt16(); // 0x0500 | Just on case 0x0400 (MUSIC000.DAT), maybe it refer to the WAVE0005.DAT sample collection buffer.ReadBytes(6); // padding uint unk1 = buffer.ReadUInt32(); // never > 127, maybe a general volume ? or something like that uint sampleSet = buffer.ReadUInt32(); // ID of the WAVE*.DAT in the SOUND folder buffer.ReadBytes(8); // padding int bnumt = buffer.ReadInt32(); uint numTrack = ToolBox.GetNumPositiveBits(bnumt); short unk3 = buffer.ReadInt16(); // (0, -1, 255 or 16383) from MUSIC050 to MUSIC101 => unk3 != 0 // when != 0 it seems like it's not a "music" but more like a sounds store for maps ambiance or monsters // when != 0, in most of case there is no instruments set nor a drum (excp 68, 69) and Unknowns AKAO events occur a lot. // in these cases you really feal that it doesn't really make sence to out a midi file and less a wav... // when != 0 => sampleSet 17 to 25 // Case 255 (66 to 73, 82, 83, 96, 97) // Case 16383 (78 to 81, 88 to 91) // Case -1 all others from 50 to 101 buffer.ReadBytes(10); // padding uint ptr1 = buffer.ReadUInt32() + 0x30; // instruments pointer uint ptr2 = buffer.ReadUInt32() + 0x34; // Drums pointer buffer.ReadBytes(8); // padding ushort jump = buffer.ReadUInt16(); long basePtr = buffer.BaseStream.Position; long musInstrPtr = buffer.BaseStream.Position + jump - 2; if (true) { Debug.Log(string.Concat("AKAO from : ", FileName, " FileSize = ", FileSize, " | reverb : ", reverb, " numTrack : ", numTrack, " sampleSet : ", sampleSet, "\r\ninstruments at : ", ptr1, " Drums at : ", ptr2, " musInstrPtr : ", musInstrPtr, " | unk1 : ", unk1, " unk3 : ", unk3)); } ushort[] tracksPtr = new ushort[numTrack]; tracksPtr[0] = (ushort)musInstrPtr; for (uint i = 0; i < numTrack - 1; i++) { tracksPtr[i + 1] = (ushort)((basePtr + i * 2) + buffer.ReadUInt16()); } // music instuctions begin here, MIDI like format, we don't care yet, so let's jump uint instrCount = 0; // Instruments if (ptr1 > 0x30) { buffer.BaseStream.Position = ptr1; // Instruments Header always 0x20 ? List <ushort> instrPtrs = new List <ushort>(); for (int i = 0; i < 0x10; i++) { ushort instrPtr = buffer.ReadUInt16(); if (instrPtr != 0xFFFF) { instrPtrs.Add(instrPtr); } else { // Padding } } instrCount = (uint)instrPtrs.Count; if (ptr2 > 0x34) { instrCount++; } if (UseDebug) { Debug.Log("Instruments number : " + instrCount); } instruments = new List <AKAOInstrument>(); for (int i = 0; i < instrPtrs.Count; i++) { AKAOInstrument instrument = new AKAOInstrument((uint)i, AKAOInstrument.InstrumentType.INSTR_MELODIC); instrument.name = "Instrument #" + (ushort)i; long instrStart = ptr1 + 0x20 + instrPtrs[i]; long instrEnd; if (i < instrPtrs.Count - 1) { instrEnd = ptr1 + 0x20 + instrPtrs[i + 1]; } else { if (ptr2 > 0x34) { instrEnd = ptr2; } else { instrEnd = byteLen; } } int instrRegLoop = (int)(instrEnd - instrStart) / 0x08; if (UseDebug) { Debug.Log(string.Concat("Instrument #", i, " Regions count : ", instrRegLoop - 1)); } instrument.regions = new AKAORegion[instrRegLoop - 1]; // -1 because the last 8 bytes are padding for (int j = 0; j < instrRegLoop - 1; j++) { AKAORegion reg = new AKAORegion(); reg.FeedMelodic(buffer.ReadBytes(8)); instrument.regions[j] = reg; if (UseDebug) { Debug.Log(reg.ToString()); } } buffer.ReadBytes(8); // 0000 0000 0000 0000 padding instruments.Add(instrument); } } // Drum if (ptr2 > 0x34) { if (buffer.BaseStream.Position != ptr2) { buffer.BaseStream.Position = ptr2; } // Special case when there is no melodic instruments if (instruments == null) { instrCount++; instruments = new List <AKAOInstrument>(); } AKAOInstrument drum = new AKAOInstrument(instrCount - 1, AKAOInstrument.InstrumentType.INSTR_DRUM); drum.name = "Drum"; int drumRegLoop = (int)(byteLen - ptr2) / 0x08; if (UseDebug) { Debug.Log(string.Concat("Drum Regions count : ", drumRegLoop - 1)); } List <AKAORegion> dr = new List <AKAORegion>(); for (int j = 0; j < drumRegLoop - 1; j++) { byte[] b = buffer.ReadBytes(8); if (b[0] == 0xFF && b[1] == 0xFF && b[2] == 0xFF && b[3] == 0xFF && b[4] == 0xFF && b[5] == 0xFF && b[6] == 0xFF && b[7] == 0xFF) { break; } if (b[0] > 0 && b[1] > 0 && b[6] > 0 && b[7] > 0) { AKAORegion dregion = new AKAORegion(); dregion.FeedDrum(b, j); dr.Add(dregion); if (UseDebug) { Debug.Log(dregion.ToString()); } } } drum.regions = dr.ToArray(); instruments.Add(drum); } long end = 0; if (ptr1 > 0x30) { end = ptr1; } else if (ptr2 > 0x34) { end = ptr2; } else { end = buffer.BaseStream.Length; } composer = new AKAOComposer(buffer, musInstrPtr, end, instrCount, numTrack, tracksPtr, FileName, true); // AKAO from : WAVE0000 startingArticulationId = 0 // AKAO from : WAVE0005 startingArticulationId = 32 // AKAO from : WAVE0032 startingArticulationId = 32 // All other startingArticulationId = 64 // AKAO from : WAVE0200 startingArticulationId = 128 // So we seek for the appropriate WAVE*.DAT in the SOUND folder string[] hash = FilePath.Split("/"[0]); hash[hash.Length - 2] = "SOUND"; AKAO[] sampleCollections = new AKAO[3]; // Program from 32 to 63 hash[hash.Length - 1] = "WAVE0005.DAT"; // wave 005 or wave 032 ? (5 seems good) AKAO SampleColl32 = new AKAO(); SampleColl32.UseDebug = UseDebug; SampleColl32.Parse(String.Join("/", hash), AKAO.SOUND); sampleCollections[0] = SampleColl32; string zz = "0"; if (sampleSet < 100) { zz += "0"; } if (sampleSet < 10) { zz += "0"; } hash[hash.Length - 1] = "WAVE" + zz + sampleSet + ".DAT"; string samplePath = String.Join("/", hash); bool test = File.Exists(samplePath); // Program from 64 to 95 or 127 AKAO SampleColl64 = new AKAO(); SampleColl64.UseDebug = UseDebug; SampleColl64.Parse(samplePath, AKAO.SOUND); sampleCollections[1] = SampleColl64; // Additionnal Collection, somztimes usefull for drum kit or A1 program change if (SampleColl64.articulations.Length < 64) { hash[hash.Length - 1] = "WAVE0091.DAT"; AKAO addiColl = new AKAO(); addiColl.UseDebug = UseDebug; addiColl.Parse(String.Join("/", hash), AKAO.SOUND); sampleCollections[2] = addiColl; } if (composer.A1Calls.Count > 0) { // we need to add new instruments with an unique region foreach (uint iid in composer.A1Calls) { AKAOInstrument A1Instrument = new AKAOInstrument(iid); A1Instrument.name = "A1 Instrument #" + (ushort)iid; A1Instrument.a1 = true; A1Instrument.regions = new AKAORegion[1]; AKAORegion defaultRegion = new AKAORegion(); defaultRegion.articulationId = (byte)iid; A1Instrument.regions[0] = defaultRegion; if (instruments == null) { instruments = new List <AKAOInstrument>(); } instruments.Add(A1Instrument); } } SF2 sf2 = null; if (bWAV || bSF2 || bDLS) { sf2 = SoundFundry(this, sampleCollections); } if (bMID || bWAV) { if (unk3 != 0) { bWAV = false; // we don't want 1Gb crap .wav } composer.Synthetize(bMID, bWAV, sf2); } break; case AKAOType.SOUND: // Samples Collection // header datas header = buffer.ReadBytes(4); // AKAO ushort sampleId = buffer.ReadUInt16(); buffer.ReadBytes(10); // padding unk1 = buffer.ReadByte(); // almost always 0 (but one case 48 in WAVE0032) unk3 = buffer.ReadByte(); // almost always 81 (two cases 49 (WAVE0000, WAVE0005), one case 16 in WAVE0032, one case 177 in WAVE0200) buffer.ReadBytes(2); // padding var sampleSize = buffer.ReadUInt32(); startingArticulationId = buffer.ReadUInt32(); var numArts = buffer.ReadUInt32(); // mostly 32, sometimes 64, one case 48 (WAVE0071), one case 96 (WAVE0200) /* List of 64 arts * WAVE0044 * WAVE0045 * WAVE0046 * WAVE0053 * WAVE0054 * WAVE0055 * WAVE0064 * WAVE0065 * WAVE0068 * WAVE0069 * WAVE0091 * WAVE0097 * WAVE0099 */ buffer.ReadBytes(32); // padding if (UseDebug) { Debug.Log(string.Concat("AKAO from : ", FileName, " len = ", FileSize, " ID : ", sampleId, " unk1 : ", unk1, " unk3 : ", unk3, " sampleSize : ", sampleSize, " stArtId : ", startingArticulationId, " numArts : ", numArts)); } // Articulations section here articulations = new AKAOArticulation[numArts]; for (uint i = 0; i < numArts; i++) { AKAOArticulation arti = new AKAOArticulation(buffer, startingArticulationId + i); articulations[i] = arti; if (UseDebug) { //Debug.Log(arti.ToString()); } } // Samples section here ulong samStart = (ulong)buffer.BaseStream.Position; // First we need to determine the start and the end of the samples, 16 null bytes indicate a new sample, so lets find them. List <long> samPtr = new List <long>(); List <long> samEPtr = new List <long>(); while (buffer.BaseStream.Position < buffer.BaseStream.Length) { if (buffer.ReadUInt64() + buffer.ReadUInt64() == 0) { if (samPtr.Count > 0) { //samEPtr.Add(buffer.BaseStream.Position - 0x20); samEPtr.Add(buffer.BaseStream.Position - 0x10); } samPtr.Add(buffer.BaseStream.Position - 0x10); //samPtr.Add(buffer.BaseStream.Position); } } samEPtr.Add(buffer.BaseStream.Length); // Let's loop again to get samples int numSam = samPtr.Count; samples = new AKAOSample[numSam]; for (int i = 0; i < numSam; i++) { buffer.BaseStream.Position = samPtr[i]; int size = (int)(samEPtr[i] - samPtr[i]); byte[] dt = buffer.ReadBytes(size); AKAOSample sam = new AKAOSample(string.Concat(FileName, " Sample #", (ushort)i), dt, (ulong)samPtr[i]); sam.index = i; samples[i] = sam; if (UseDebug && bWAV) { WAV wavSam = sam.ConvertToWAV(); wavSam.SetName(FileName + "_Sample_" + i); ToolBox.DirExNorCreate(Application.dataPath + "/../Assets/Resources/Sounds/SampleColl/"); wavSam.WriteFile(Application.dataPath + "/../Assets/Resources/Sounds/SampleColl/" + FileName + "_Sample_" + i + ".wav", wavSam.Write()); } } // now to verify and associate each articulation with a sample index value // for every sample of every instrument, we add sample_section offset, because those values // are relative to the beginning of the sample section for (uint i = 0; i < articulations.Length; i++) { for (uint l = 0; l < samples.Length; l++) { //if (articulations[i].sampleOff + samStart + 0x10 == samples[l].offset) if (articulations[i].sampleOff + samStart == samples[l].offset) { articulations[i].sampleNum = l; articulations[i].sample = samples[l]; samples[l].loopStart = articulations[i].loopPt; break; } } } break; case AKAOType.PROG: // unknown yet header = buffer.ReadBytes(4); // AKAO ushort id = buffer.ReadUInt16(); buffer.ReadUInt16(); ushort tp = buffer.ReadUInt16(); buffer.ReadUInt16(); buffer.ReadUInt32(); buffer.ReadUInt32(); buffer.ReadUInt32(); buffer.ReadUInt32(); buffer.ReadUInt32(); buffer.ReadUInt16(); byteLen = buffer.ReadUInt16(); //Debug.Log(string.Concat("AKAO PROG : id : ", id, " tp : ", tp, " byteLen : ", byteLen)); //composer = new AKAOComposer(buffer, buffer.BaseStream.Position, limit, 0, 1, new ushort[] { (ushort)buffer.BaseStream.Position }, FileName, true); //composer.Synthetize(true, false); break; case AKAOType.EFFECT: //https://github.com/vgmtrans/vgmtrans/blob/akao-ps1/src/main/formats/AkaoSeq.cpp header = buffer.ReadBytes(4); // AKAO id = buffer.ReadUInt16(); byteLen = buffer.ReadUInt16(); reverb = buffer.ReadUInt16(); bnumt = buffer.ReadInt32(); numTrack = ToolBox.GetNumPositiveBits(bnumt); buffer.ReadBytes(6); Debug.Log(string.Concat("AKAO EFFECT : id : ", id, " byteLen : ", byteLen, " reverb : ", reverb, " numTrack : ", numTrack)); composer = new AKAOComposer(buffer, buffer.BaseStream.Position, limit, 0, 1, new ushort[] { (ushort)buffer.BaseStream.Position }, FileName, true); Debug.Log(string.Concat("composer.A1Calls.Count : ", composer.A1Calls.Count)); composer.Synthetize(true, false); break; case AKAOType.SAMPLE: // similar to AKAOType.SOUND without articulations, we can output a WAV file // header datas header = buffer.ReadBytes(4); // AKAO buffer.ReadUInt16(); buffer.ReadBytes(10); // padding buffer.ReadUInt16(); // buffer.ReadBytes(2); // padding buffer.ReadUInt32(); buffer.ReadUInt32(); buffer.ReadUInt32(); buffer.ReadBytes(32); // padding buffer.ReadBytes(16); // sample padding AKAOSample sample = new AKAOSample(FileName, buffer.ReadBytes((int)(limit - buffer.BaseStream.Position)), (ulong)buffer.BaseStream.Position); WAV nw = sample.ConvertToWAV(); nw.SetName(FileName); if (UseDebug && bWAV) { ToolBox.DirExNorCreate(Application.dataPath + "/../Assets/Resources/Sounds/Effects/"); nw.WriteFile(Application.dataPath + "/../Assets/Resources/Sounds/Effects/" + FileName + ".wav", nw.Write()); } break; } }
public void Parse(BinaryReader buffer) { ptrRoomSection = buffer.ReadUInt32(); lenRoomSection = buffer.ReadUInt32(); ptrClearedSection = buffer.ReadUInt32(); lenClearedSection = buffer.ReadUInt32(); ptrScriptSection = buffer.ReadUInt32(); lenScriptSection = buffer.ReadUInt32(); ptrDoorSection = buffer.ReadUInt32(); lenDoorSection = buffer.ReadUInt32(); ptrEnemySection = buffer.ReadUInt32(); lenEnemySection = buffer.ReadUInt32(); ptrTreasureSection = buffer.ReadUInt32(); lenTreasureSection = buffer.ReadUInt32(); // Room sub sections lenGeometrySection = buffer.ReadUInt32(); lenCollisionSection = buffer.ReadUInt32(); lenSubSection03 = buffer.ReadUInt32(); lenRoomDoorSection = buffer.ReadUInt32(); lenLightingSection = buffer.ReadUInt32(); lenSubSection06 = buffer.ReadUInt32(); lenSubSection07 = buffer.ReadUInt32(); lenSubSection08 = buffer.ReadUInt32(); lenSubSection09 = buffer.ReadUInt32(); lenSubSection0A = buffer.ReadUInt32(); lenSubSection0B = buffer.ReadUInt32(); lenTextureEffectsSection = buffer.ReadUInt32(); lenSubSection0D = buffer.ReadUInt32(); lenSubSection0E = buffer.ReadUInt32(); lenSubSection0F = buffer.ReadUInt32(); lenSubSection10 = buffer.ReadUInt32(); lenSubSection11 = buffer.ReadUInt32(); lenSubSection12 = buffer.ReadUInt32(); lenSubSection13 = buffer.ReadUInt32(); lenAKAOSubSection = buffer.ReadUInt32(); lenSubSection15 = buffer.ReadUInt32(); lenSubSection16 = buffer.ReadUInt32(); lenSubSection17 = buffer.ReadUInt32(); lenSubSection18 = buffer.ReadUInt32(); if (UseDebug) { Debug.Log("MPD parse : " + filePath); Debug.Log("ptrRoomSection :" + ptrRoomSection + " lenRoomSection : " + lenRoomSection); Debug.Log("ptrClearedSection :" + ptrClearedSection + " lenClearedSection : " + lenClearedSection); Debug.Log("ptrScriptSection :" + ptrScriptSection + " lenScriptSection : " + lenScriptSection); Debug.Log("ptrDoorSection :" + ptrDoorSection + " lenDoorSection : " + lenDoorSection); Debug.Log("ptrEnemySection :" + ptrEnemySection + " lenEnemySection : " + lenEnemySection); Debug.Log("ptrTreasureSection :" + ptrTreasureSection + " lenTreasureSection : " + lenTreasureSection); Debug.Log("lenGeometrySection :" + lenGeometrySection); Debug.Log("lenCollisionSection :" + lenCollisionSection); Debug.Log("lenSubSection03 :" + lenSubSection03); Debug.Log("lenRoomDoorSection :" + lenRoomDoorSection); Debug.Log("lenLightingSection :" + lenLightingSection); Debug.Log("lenSubSection06 :" + lenSubSection06); Debug.Log("lenSubSection07 :" + lenSubSection07); Debug.Log("lenSubSection08 :" + lenSubSection08); Debug.Log("lenSubSection09 :" + lenSubSection09); Debug.Log("lenSubSection0A :" + lenSubSection0A); Debug.Log("lenSubSection0B :" + lenSubSection0B); Debug.Log("lenTextureEffectsSection :" + lenTextureEffectsSection); Debug.Log("lenSubSection0D :" + lenSubSection0D); Debug.Log("lenSubSection0E :" + lenSubSection0E); Debug.Log("lenSubSection0F :" + lenSubSection0F); Debug.Log("lenSubSection10 :" + lenSubSection10); Debug.Log("lenSubSection11 :" + lenSubSection11); Debug.Log("lenSubSection12 :" + lenSubSection12); Debug.Log("lenSubSection13 :" + lenSubSection13); Debug.Log("lenAKAOSubSection :" + lenAKAOSubSection); Debug.Log("lenSubSection15 :" + lenSubSection15); Debug.Log("lenSubSection16 :" + lenSubSection16); Debug.Log("lenSubSection17 :" + lenSubSection17); Debug.Log("lenSubSection18 :" + lenSubSection18); } // ROOM section if (UseDebug) { Debug.Log("ROOM section : " + buffer.BaseStream.Position); } // Geometry if (lenRoomSection > 4) { if (lenGeometrySection > 0) { numGroups = buffer.ReadUInt32(); if (UseDebug) { Debug.Log("numGroups : " + numGroups); } groups = new MPDGroup[numGroups]; for (uint i = 0; i < numGroups; i++) { groups[i] = new MPDGroup(); groups[i].header = buffer.ReadBytes(64); if ((groups[i].header[1] & 0x08) > 0) { groups[i].scale = 1; } } for (uint i = 0; i < numGroups; i++) { uint numTriangles = buffer.ReadUInt32(); uint numQuads = buffer.ReadUInt32(); for (uint j = 0; j < numTriangles; j++) { MPDFace face = new MPDFace(groups[i], false); face.feed(buffer); MPDMesh m = groups[i].getMesh(face.textureId, face.clutId); m.addFace(face); } for (uint j = 0; j < numQuads; j++) { MPDFace face = new MPDFace(groups[i], true); face.feed(buffer); MPDMesh m = groups[i].getMesh(face.textureId, face.clutId); m.addFace(face); } } } // collision if (lenCollisionSection > 0) { long collisionPtr = buffer.BaseStream.Position; uint TyleWidth = buffer.ReadUInt16(); uint TyleHeight = buffer.ReadUInt16(); if (UseDebug) { Debug.Log("TyleWidth : " + TyleWidth + " TyleHeight : " + TyleHeight); } uint unk1 = buffer.ReadUInt16(); uint unk2 = buffer.ReadUInt16(); uint[] FloorHeight = new uint[TyleWidth * TyleHeight]; uint[] CeilingHeight = new uint[TyleWidth * TyleHeight]; uint[] Incline = new uint[TyleWidth * TyleHeight]; //Debug.Log("Collision ptr : " + buffer.BaseStream.Position); for (uint i = 0; i < TyleWidth * TyleHeight; i++) { FloorHeight[i] = buffer.ReadUInt16(); if (UseDebug) { //Debug.Log("FloorHeight[i] : " + FloorHeight[i]); } } for (uint i = 0; i < TyleWidth * TyleHeight; i++) { CeilingHeight[i] = buffer.ReadUInt16(); if (UseDebug) { //Debug.Log("CeilingHeight[i] : " + CeilingHeight[i]); } } for (uint i = 0; i < TyleWidth * TyleHeight / 2; i++) { byte b = buffer.ReadByte(); Incline[i * 2] = (uint)b << 4; Incline[i * 2 + 1] = (uint)b >> 4; } buffer.BaseStream.Position = collisionPtr + lenCollisionSection; } // section 3 ?? // if (lenSubSection03 > 0) { Debug.Log("lenSubSection03 ptr : " + buffer.BaseStream.Position + " lenSubSection03 : " + lenSubSection03); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection03; } // door section if (lenRoomDoorSection > 0) { long doorPtr = buffer.BaseStream.Position; uint numDoors = lenRoomDoorSection / 0x0C; doors = new MPDDoor[numDoors]; for (uint i = 0; i < numDoors; i++) { MPDDoor d = new MPDDoor(); d.destZone = buffer.ReadByte(); d.destRoom = buffer.ReadByte(); d.unkn = buffer.ReadBytes(6); d.idCurrentDoor = buffer.ReadUInt32(); doors[i] = d; if (UseDebug) { Debug.Log("MPDDoor # " + i + " idCurrentDoor : " + d.idCurrentDoor + " destZone : " + d.destZone + " destRoom : " + d.destRoom); Debug.Log(d.unkn[0] + ", " + d.unkn[1] + ", " + d.unkn[2] + ", " + d.unkn[3] + ", " + d.unkn[4] + ", " + d.unkn[5]); } } buffer.BaseStream.Position = doorPtr + lenRoomDoorSection; } // lights section // http://www.psxdev.net/forum/viewtopic.php?f=51&t=3383 if (lenLightingSection > 0) { long lightPtr = buffer.BaseStream.Position; if (UseDebug) { Debug.Log("lights ptr : " + lightPtr + " lenLightingSection : " + lenLightingSection + "(" + (lightPtr + lenLightingSection) + ")"); } Color32 mc = new Color32(buffer.ReadByte(), buffer.ReadByte(), buffer.ReadByte(), buffer.ReadByte()); uint numLights = buffer.ReadUInt32(); if (UseDebug) { Debug.Log("numLights : " + numLights + " mainColor : " + mc.ToString()); } buffer.ReadUInt32(); // padding lights = new List <GameObject>(); string lightsDebug = ""; for (uint i = 0; i < numLights; i++) { string lgtMat = ""; short[] matrix = new short[10]; byte[] hexa = buffer.ReadBytes(20); buffer.BaseStream.Position -= 20; for (uint j = 0; j < 10; j++) { matrix[j] = buffer.ReadInt16(); lgtMat += (matrix[j]) + " | "; } byte[] cols = buffer.ReadBytes(12); Color32 colorX = new Color32(cols[0], cols[1], cols[2], cols[3]); Color32 colorY = new Color32(cols[4], cols[5], cols[6], cols[7]); Color32 colorZ = new Color32(cols[8], cols[9], cols[10], cols[11]); Color32 main = Color.black; if (colorX.r != mc.r && colorX.g != mc.g && colorX.b != mc.b) { main = colorX; } if (colorY.r != mc.r && colorY.g != mc.g && colorY.b != mc.b) { main = colorY; } if (colorZ.r != mc.r && colorZ.g != mc.g && colorZ.b != mc.b) { main = colorZ; } main.a = 255; lightsDebug += string.Concat("Light # ", i, " : ", BitConverter.ToString(hexa), " -> ", lgtMat, " | ", colorX, ", ", colorY, ", ", colorZ, "\r\n"); GameObject lgo = new GameObject("Point Light"); Rect lightRect = new Rect(); lightRect.xMin = -matrix[0] / 100; lightRect.yMin = -matrix[1] / 100; lightRect.xMax = -matrix[2] / 100; lightRect.yMax = -matrix[3] / 100; // i need to find a way to get Y axe lgo.transform.position = new Vector3(lightRect.center.x, 5f, lightRect.center.y); lgo.transform.localScale = Vector3.one; Light l = lgo.AddComponent <Light>(); l.name = "l" + i; l.type = LightType.Point; l.range = Vector2.Distance(lightRect.center, lightRect.min); l.intensity = 2f; l.color = main; l.shadows = LightShadows.Soft; lights.Add(lgo); } if (UseDebug) { Debug.Log(lightsDebug); } if (UseDebug) { Debug.Log("end lights : " + buffer.BaseStream.Position); } buffer.BaseStream.Position = lightPtr + lenLightingSection; } if (lenSubSection06 > 0) { Debug.Log("SubSection06 ptr : " + buffer.BaseStream.Position + " lenSubSection06 : " + lenSubSection06); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection06; } if (lenSubSection07 > 0) { Debug.Log("SubSection07 ptr : " + buffer.BaseStream.Position + " lenSubSection07 : " + lenSubSection07); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection07; } if (lenSubSection08 > 0) { Debug.Log("SubSection08 ptr : " + buffer.BaseStream.Position + " lenSubSection08 : " + lenSubSection08); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection08; } if (lenSubSection09 > 0) { Debug.Log("SubSection09 ptr : " + buffer.BaseStream.Position + " lenSubSection09 : " + lenSubSection09); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection09; } if (lenSubSection0A > 0) { Debug.Log("SubSection0A ptr : " + buffer.BaseStream.Position + " lenSubSection0A : " + lenSubSection0A); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection0A; } if (lenSubSection0B > 0) { Debug.Log("SubSection0B ptr : " + buffer.BaseStream.Position + " lenSubSection0B : " + lenSubSection0B); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection0B; } if (lenTextureEffectsSection > 0) { Debug.Log("TextureEffectsSection ptr : " + buffer.BaseStream.Position + " lenTextureEffectsSection : " + lenTextureEffectsSection); buffer.BaseStream.Position = buffer.BaseStream.Position + lenTextureEffectsSection; } if (lenSubSection0D > 0) { Debug.Log("SubSection0D ptr : " + buffer.BaseStream.Position + " lenSubSection0D : " + lenSubSection0D); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection0D; } if (lenSubSection0E > 0) { Debug.Log("SubSection0E ptr : " + buffer.BaseStream.Position + " lenSubSection0E : " + lenSubSection0E); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection0E; } if (lenSubSection0F > 0) { Debug.Log("SubSection0F ptr : " + buffer.BaseStream.Position + " lenSubSection0F : " + lenSubSection0F); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection0F; } if (lenSubSection10 > 0) { Debug.Log("SubSection10 ptr : " + buffer.BaseStream.Position + " lenSubSection10 : " + lenSubSection10); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection10; } if (lenSubSection11 > 0) { Debug.Log("SubSection11 ptr : " + buffer.BaseStream.Position + " lenSubSection11 : " + lenSubSection11); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection11; } if (lenSubSection12 > 0) { Debug.Log("SubSection12 ptr : " + buffer.BaseStream.Position + " lenSubSection12 : " + lenSubSection12); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection12; } if (lenSubSection13 > 0) { Debug.Log("SubSection13 ptr : " + buffer.BaseStream.Position + " lenSubSection13 : " + lenSubSection13); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection13; } if (lenAKAOSubSection > 0) { long akaoPtr = buffer.BaseStream.Position; if (UseDebug) { Debug.Log("akaoPtr : " + akaoPtr + " lenAKAOSubSection : " + lenAKAOSubSection + "(" + (akaoPtr + lenAKAOSubSection) + ")"); } buffer.ReadUInt32(); // 0200 0000 buffer.ReadUInt32(); // 0000 0000 buffer.ReadUInt32(); // 0C00 0000 AKAO audio = new AKAO(); audio.FileName = FileName; audio.UseDebug = true; audio.Parse(buffer, AKAO.UNKNOWN, akaoPtr + lenAKAOSubSection); buffer.BaseStream.Position = akaoPtr + lenAKAOSubSection; } if (lenSubSection15 > 0) { Debug.Log("SubSection15 ptr : " + buffer.BaseStream.Position + " lenSubSection15 : " + lenSubSection15); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection15; } if (lenSubSection16 > 0) { Debug.Log("SubSection16 ptr : " + buffer.BaseStream.Position + " lenSubSection16 : " + lenSubSection16); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection16; } if (lenSubSection17 > 0) { Debug.Log("SubSection17 ptr : " + buffer.BaseStream.Position + " lenSubSection17 : " + lenSubSection17); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection17; } if (lenSubSection18 > 0) { Debug.Log("SubSection18 ptr : " + buffer.BaseStream.Position + " lenSubSection18 : " + lenSubSection18); buffer.BaseStream.Position = buffer.BaseStream.Position + lenSubSection18; } } else { // No geometry :s _geom = false; } // Cleared section if (buffer.BaseStream.Position != ptrClearedSection) { buffer.BaseStream.Position = ptrClearedSection; } if (UseDebug) { Debug.Log("Cleared section : " + buffer.BaseStream.Position); } // Script section if (buffer.BaseStream.Position != ptrScriptSection) { buffer.BaseStream.Position = ptrScriptSection; } if (UseDebug) { Debug.Log("Script section : " + buffer.BaseStream.Position); } // See Opcode.cs // Door section if (buffer.BaseStream.Position != ptrDoorSection) { buffer.BaseStream.Position = ptrDoorSection; } if (UseDebug) { Debug.Log("Door section : " + buffer.BaseStream.Position); } if (lenDoorSection > 0) { } // Ennemy section if (buffer.BaseStream.Position != ptrEnemySection) { buffer.BaseStream.Position = ptrEnemySection; } if (UseDebug) { Debug.Log("Ennemy section : " + buffer.BaseStream.Position); } // Treasure section if (buffer.BaseStream.Position != ptrTreasureSection) { buffer.BaseStream.Position = ptrTreasureSection; } if (UseDebug) { Debug.Log("Treasure section : " + buffer.BaseStream.Position); } buffer.Close(); }
public void Parse(BinaryReader buffer) { byte[] header = buffer.ReadBytes(4); numBones = buffer.ReadByte(); numGroups = buffer.ReadByte(); numTriangles = buffer.ReadUInt16(); numQuads = buffer.ReadUInt16(); numPolys = buffer.ReadUInt16(); numFaces = numTriangles + numQuads + numPolys; byte[][] overlays = new byte[8][]; for (int i = 0; i < 8; i++) { overlays[i] = buffer.ReadBytes(4); } buffer.ReadBytes(0x24); // Unknown buffer.ReadBytes(6); // collision size and height (shape is a cylinder) buffer.ReadBytes(2); // menu position Y buffer.ReadBytes(12); // Unknown buffer.ReadBytes(2); // Shadow radius buffer.ReadBytes(2); // Shadow size increase rate buffer.ReadBytes(2); // Shadow size decrease rate buffer.ReadBytes(4); // Unknown buffer.ReadBytes(2); // Menu scale buffer.ReadBytes(2); // Unknown buffer.ReadBytes(2); // Target sphere position Y buffer.ReadBytes(8); // Unknown if (UseDebug) { Debug.Log(FileName); Debug.Log("numBones : " + numBones); Debug.Log("numGroups : " + numGroups); Debug.Log("numTriangles : " + numTriangles); Debug.Log("numQuads : " + numQuads); Debug.Log("numPolys : " + numPolys); Debug.Log("numFaces : " + numFaces); } // LBA XX_BTX.SEQ (battle animations first one is actually XX_COM.SEQ) certainly one weapon type for each file /* * LBA SEQ : 98646 OBJ\01_COM.SEQ * LBA SEQ : 0 * LBA SEQ : 98599 OBJ\01_BT2.SEQ * LBA SEQ : 0 * LBA SEQ : 98611 OBJ\01_BT4.SEQ * LBA SEQ : 0 * LBA SEQ : 98623 OBJ\01_BT6.SEQ * LBA SEQ : 0 * LBA SEQ : 0 * LBA SEQ : 0 * LBA SEQ : 98635 OBJ\01_BTA.SEQ * LBA SEQ : 0 */ for (int i = 0; i < 12; i++) { buffer.ReadUInt32(); } for (int i = 0; i < 12; i++) { buffer.ReadBytes(2); // chain attack animation ID } for (int i = 0; i < 4; i++) { buffer.ReadBytes(4); // LBA XXSP0X.SEQ (special attack animations) } buffer.ReadBytes(0x20); // unknown (probably more LBA tables, there are also special attack ids stored here.) file like 00BT1B00.SEQ (Battle Techniques) long dec = buffer.BaseStream.Position + 4; long magicPtr = buffer.ReadUInt32() + dec; for (int i = 0; i < 0x18; i++) { buffer.ReadBytes(2); // unknown (noticeable effects when casting spell } long AKAOPtr = buffer.ReadUInt32() + dec; long groupPtr = buffer.ReadUInt32() + dec; long vertexPtr = buffer.ReadUInt32() + dec; long facePtr = buffer.ReadUInt32() + dec; if (UseDebug) { Debug.Log("magicPtr : " + magicPtr); Debug.Log("AKAOPtr : " + AKAOPtr); Debug.Log("groupPtr : " + groupPtr); Debug.Log("vertexPtr : " + vertexPtr); Debug.Log("facePtr : " + facePtr); } // Bones section bones = new List <VSBone>(); for (uint i = 0; i < numBones; i++) { VSBone bone = new VSBone(); bone.index = i; bone.name = "bone_" + i; bone.length = buffer.ReadInt16(); //Debug.LogWarning("bone "+i+ " .length : "+ bone.length); buffer.ReadUInt16(); // always 0xFFFF bone.parentIndex = buffer.ReadByte(); if (bone.parentIndex > numBones) { bone.parentIndex = -1; } //Debug.LogWarning("bone.parentIndex : " + bone.parentIndex); // https://github.com/morris/vstools/blob/master/src/WEPBone.js byte[] offset = buffer.ReadBytes(3); bone.offset = new Vector3(offset[0], offset[1], offset[2]); bone.mode = buffer.ReadByte(); // 0 - 2 normal ? // 3 - 6 normal + roll 90 degrees // 7 - 255 absolute, different angles buffer.ReadBytes(7); // always 0000000 bones.Add(bone); } // Group section if (buffer.BaseStream.Position != groupPtr) { if (UseDebug) { Debug.Log(buffer.BaseStream.Position + " != " + groupPtr); Debug.Log("le pointeur groupPtr n'est pas à la bonne place"); } buffer.BaseStream.Position = groupPtr; } groups = new List <VSGroup>(); for (uint i = 0; i < numGroups; i++) { VSGroup group = new VSGroup(); group.boneIndex = buffer.ReadInt16(); group.numVertices = buffer.ReadUInt16(); if (group.boneIndex != -1) { group.bone = bones[group.boneIndex]; } groups.Add(group); } // Vertices section if (buffer.BaseStream.Position != vertexPtr) { if (UseDebug) { Debug.Log(buffer.BaseStream.Position + " != " + vertexPtr); Debug.Log("le pointeur vertexPtr n'est pas à la bonne place"); } buffer.BaseStream.Position = vertexPtr; } vertices = new List <VSVertex>(); uint numVertices = groups[groups.Count - 1].numVertices; if (UseDebug) { Debug.Log("numVertices : " + numVertices); } int g = 0; for (uint i = 0; i < numVertices; i++) { if (i >= groups[g].numVertices) { g++; } VSVertex vertex = new VSVertex(); vertex.group = groups[g]; vertex.bone = vertex.group.bone; BoneWeight bw = new BoneWeight(); bw.boneIndex0 = (int)vertex.group.bone.index; bw.weight0 = 1; vertex.boneWeight = bw; int x = buffer.ReadInt16(); int y = buffer.ReadInt16(); int z = buffer.ReadInt16(); buffer.ReadInt16(); vertex.position = -new Vector3(x, y, z) / 100; vertices.Add(vertex); } // Polygone section if (buffer.BaseStream.Position != facePtr) { if (UseDebug) { Debug.Log(buffer.BaseStream.Position + " != " + facePtr); Debug.Log("le pointeur facePtr n'est pas à la bonne place"); } buffer.BaseStream.Position = facePtr; } faces = new List <VSFace>(); if (excpFaces) { if (UseDebug) { Debug.LogWarning("------------- TRIANGLES ----------------------"); Debug.LogWarning(buffer.BaseStream.Position); } for (uint i = 0; i < numTriangles; i++) { faces.Add(ParseColoredFace(buffer, 3)); } if (UseDebug) { Debug.LogWarning("------------- QUAD ----------------------"); Debug.LogWarning(buffer.BaseStream.Position); } for (uint i = 0; i < numQuads; i++) { faces.Add(ParseColoredFace(buffer, 4)); } if (UseDebug) { Debug.LogWarning("------------- POLY ----------------------"); Debug.LogWarning(buffer.BaseStream.Position); } for (uint i = 0; i < numPolys; i++) { long polyDec = buffer.BaseStream.Position; byte[] bytes = buffer.ReadBytes(24); if (bytes[11] == 0x34) { // its a triangle buffer.BaseStream.Position = polyDec; faces.Add(ParseColoredFace(buffer, 3)); } if (bytes[11] == 0x3C) { // its a quad buffer.BaseStream.Position = polyDec; faces.Add(ParseColoredFace(buffer, 4)); } } } else { for (uint i = 0; i < numFaces; i++) { VSFace face = new VSFace(); long polyDec = buffer.BaseStream.Position; // 4 bytes face.type = buffer.ReadByte(); face.size = buffer.ReadByte(); face.side = buffer.ReadByte(); face.alpha = buffer.ReadByte(); /* * Triangles * 24-10-04-00-C406-CC06-C806-08-78-05-6D-05-7C * 24-10-04-00-D006-D806-D406-13-6D-0A-6D-0C-78 * 24-10-04-00-D406-E006-DC06-13-78-13-6D-0F-78 * 24-10-04-00-D406-D806-E006-0F-78-13-6D-0C-78 * */ // if (UseDebug)Debug.Log(string.Concat("##### At : ", polyDec, " face# ", i, " face.type : ", face.type, " face.size : ", face.size, " face.side : ", face.side, " face.alpha : ", face.alpha)); uint[] table = new uint[256]; table[36] = 3; table[44] = 4; face.verticesCount = 0; // fallback if (table[face.type] != 0) { face.verticesCount = table[face.type]; } else { if (UseDebug) { Debug.LogError("#### Unknown face type !"); } } // 6 or 8 bytes for (uint j = 0; j < face.verticesCount; j++) { int vId = buffer.ReadUInt16() / 4; face.vertices.Add(vId); // if (UseDebug) Debug.Log("vId : " + j + " - " + vId); } // 6 or 8 bytes for (uint j = 0; j < face.verticesCount; j++) { int u = buffer.ReadByte(); int v = buffer.ReadByte(); face.uv.Add(new Vector2(u, v)); // if (UseDebug) Debug.Log("u : " + u + " v : " + v); } faces.Add(face); } } // AKAO section if (buffer.BaseStream.Position != AKAOPtr) { if (UseDebug) { Debug.LogWarning(buffer.BaseStream.Position + " != " + AKAOPtr + " le pointeur AKAOPtr n'est pas à la bonne place " + FileName); } buffer.BaseStream.Position = AKAOPtr; } uint akaoNum = buffer.ReadUInt32(); if (UseDebug) { Debug.Log(string.Concat("akaoNum : ", akaoNum)); } uint[] akaoFramesPtr = new uint[akaoNum]; // one pointer for AKAO header, a second for AKAO datas for (uint j = 0; j < akaoNum; j++) { akaoFramesPtr[j] = buffer.ReadUInt32(); } for (uint j = 0; j < akaoNum; j += 2) { long limit; if (j < akaoFramesPtr.Length - 1) { limit = AKAOPtr + akaoFramesPtr[j + 1]; // somtimes there are empty ptrs at the begining so we skip if (akaoFramesPtr[j + 1] > 0) { AKAO akao = new AKAO(); akao.FileName = string.Concat(FileName, "_Akao_", j); akao.UseDebug = true; akao.Parse(buffer, AKAO.UNKNOWN, limit); } } else { limit = magicPtr; AKAO akao = new AKAO(); akao.FileName = string.Concat(FileName, "_Akao_", j); akao.UseDebug = true; akao.Parse(buffer, AKAO.UNKNOWN, limit); } } // Magic section if (buffer.BaseStream.Position != magicPtr) { if (UseDebug) { Debug.LogWarning(buffer.BaseStream.Position + " != " + magicPtr + " le pointeur magicPtr n'est pas à la bonne place " + FileName); } buffer.BaseStream.Position = magicPtr; } long num = buffer.ReadUInt32(); long magicSize = buffer.ReadUInt32(); //size of magic effect section (doesnt include this 8 byte header) long num1 = buffer.ReadUInt32(); long num2 = buffer.ReadUInt32(); long num3 = buffer.ReadUInt32(); if (UseDebug) { Debug.Log(string.Concat("Magic num : ", num, " magicSize : ", magicSize, " num1 : " + num1, " num2 : " + num2, " num3 : " + num3)); } if (buffer.BaseStream.Position + (magicSize - 12) < buffer.BaseStream.Length) { buffer.BaseStream.Position = buffer.BaseStream.Position + (magicSize - 12); } if (UseDebug) { Debug.Log(string.Concat("buffer.BaseStream.Position : ", buffer.BaseStream.Position, " buffer.BaseStream.Length : ", buffer.BaseStream.Length)); } if (buffer.BaseStream.Position + 8 < buffer.BaseStream.Length) { // Textures section tim = new TIM(); tim.FileName = FileName; tim.ParseSHP(buffer, excpFaces); texture = tim.DrawSHP(true); } //buffer.Close(); }