diff --git a/GBAPokemonGameEditor.v12.suo b/GBAPokemonGameEditor.v12.suo index b2a350f..7c1714a 100644 Binary files a/GBAPokemonGameEditor.v12.suo and b/GBAPokemonGameEditor.v12.suo differ diff --git a/GBAPokemonGameEditor/CryFunctions.vb b/GBAPokemonGameEditor/CryFunctions.vb index a283fd8..7cd6759 100644 --- a/GBAPokemonGameEditor/CryFunctions.vb +++ b/GBAPokemonGameEditor/CryFunctions.vb @@ -240,4 +240,218 @@ Module CryFunctions End Using End Sub + Public Function ImportCry(filename As String, CryToLoad As Cry) As Cry + + 'If Cry.Offset = 0 Then + ' Return + 'End If + + ' load a wave file + Using reader = New BinaryReader(File.OpenRead(filename)) + ' read RIFF header + If reader.ReadUInt32() <> &H46464952 Then + Throw New Exception("This is not a WAVE file!") + End If + If reader.ReadInt32() + 8 <> reader.BaseStream.Length Then + Throw New Exception("Invalid file length!") + End If + If reader.ReadUInt32() <> &H45564157 Then + Throw New Exception("This is not a WAVE file!") + End If + + ' read fmt chunk + If reader.ReadUInt32() <> &H20746D66 Then + Throw New Exception("Expected fmt chunk!") + End If + If reader.ReadInt32() <> 16 Then + Throw New Exception("Invalid fmt chunk!") + End If + If reader.ReadInt16() <> 1 Then + ' only PCM format allowed + Throw New Exception("Cry must be in PCM format!") + End If + If reader.ReadInt16() <> 1 Then + ' only 1 channel allowed + Throw New Exception("Cry cannot have more than one channel!") + End If + CryToLoad.SampleRate = reader.ReadInt32() + If reader.ReadInt32() <> CryToLoad.SampleRate Then + Throw New Exception("Invalid fmt chunk!") + End If + reader.ReadUInt16() + Dim bitsPerSample = reader.ReadUInt16() + If bitsPerSample <> 8 Then + ' for now, only 8 bit PCM data + Throw New Exception("Cries must be 8-bit WAVE files! Got {bitsPerSample}-bit instead.") + End If + + ' data chunk + If reader.ReadUInt32() <> &H61746164 Then + Throw New Exception("Expected data chunk!!") + End If + Dim dataSize = reader.ReadInt32() + + CryToLoad.Data = New SByte(dataSize - 1) {} + For i As Integer = 0 To dataSize - 1 + ' read 8-bit unsigned PCM and convert to GBA signed form + CryToLoad.Data(i) = CSByte(reader.ReadByte() - 128) + Next + End Using + + ' resetting some other properties just in case + CryToLoad.Looped = False + CryToLoad.LoopStart = 0 + + Return CryToLoad + + End Function + + + Public Function SaveCry(crytosave As Cry, index As Integer, cryTable As Integer) As Boolean + + If crytosave.Offset = 0 Then + Return False + End If + 'var lookup = new byte[] { 0x0, 0x1, 0x4, 0x9, 0x10, 0x19, 0x24, 0x31, 0xC0, 0xCF, 0xDC, 0xE7, 0xF0, 0xF7, 0xFC, 0xFF }; + Dim lookup = New SByte() {0, 1, 4, 9, 16, 25, _ + 36, 49, -64, -49, -36, -25, _ + -16, -9, -4, -1} + + ' copy cry data to be written + Dim data = New List(Of Byte)() + If crytosave.Compressed Then + ' data is compressed in blocks of 1 + 0x20 bytes at a time + ' first byte is normal signed PCM data + ' following 0x20 bytes are compressed based on previous value + ' (for a value not in lookup table, closest value will be chosen instead) + 'Console.WriteLine("compressed"); + + ' each block has 0x40 samples + Dim blockCount = crytosave.Data.Length / &H40 + If crytosave.Data.Length Mod &H40 > 0 Then + blockCount += 1 + End If + + ' truncates the length of the last block + ' so we don't waste space + Dim lastBlockSize = crytosave.Data.Length - crytosave.Data.Length / &H40 * &H40 + If lastBlockSize = 0 Then + lastBlockSize = &H21 + Else + lastBlockSize = 1 + (lastBlockSize / 2) + (If(lastBlockSize Mod 2 = 0, 0, 1)) + End If + + Dim blocks = New Byte(blockCount - 1)() {} + For n As Integer = 0 To blockCount - 1 + ' create new block + If n < blockCount - 1 Then + blocks(n) = New Byte(32) {} + Else + blocks(n) = New Byte(lastBlockSize - 1) {} + End If + + Dim i As Integer = n * &H40 + Dim k As Integer = 0 + + ' set first value + blocks(n)(System.Math.Max(System.Threading.Interlocked.Increment(k), k - 1)) = CByte(crytosave.Data(i)) + Dim pcm As SByte = crytosave.Data(System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)) + + Dim j As Integer = 1 + While j < &H40 AndAlso i < crytosave.Data.Length + ' get current sample + Dim sample = crytosave.Data(System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)) + + ' difference between previous sample and this + Dim diff = sample - pcm + + ' check for a perfect match in lookup table + Dim lookupI = -1 + For x As Integer = 0 To 15 + If lookup(x) = diff Then + lookupI = x + Exit For + End If + Next + + ' search for the closest match in the table + If lookupI = -1 Then + Dim bestDiff = 255 + For x As Integer = 0 To 15 + If Math.Abs(lookup(x) - diff) < bestDiff Then + lookupI = x + bestDiff = Math.Abs(lookup(x) - diff) + End If + Next + End If + + ' set value in block + ' on an odd value, increase position in block + If j Mod 2 = 0 Then + blocks(n)(k) = blocks(n)(k) Or CByte(lookupI << 4) + Else + blocks(n)(System.Math.Max(System.Threading.Interlocked.Increment(k), k - 1)) = blocks(n)(System.Math.Max(System.Threading.Interlocked.Increment(k), k - 1)) Or CByte(lookupI) + End If + + ' set previous + pcm = sample + j += 1 + End While + Next + + For n As Integer = 0 To blockCount - 1 + data.AddRange(blocks(n)) + Next + Else + ' uncompressed, copy directly to data + 'Console.WriteLine("uncompressed"); + For Each s As SByte In crytosave.Data + data.Add(CByte(s)) + Next + End If + + ' determine if cry requires repointing + If crytosave.Size < data.Count Then + ' find a new offset for our cry + + ' overwrite old cry with FF bytes + 'rom.Seek(Cry.Offset) + 'For i As Integer = 0 To 15 + ' ' header + ' rom.WriteByte(Byte.MaxValue) + 'Next + 'For i As Integer = 0 To Cry.Size - 1 + ' ' cry data + ' rom.WriteByte(Byte.MaxValue) + 'Next + + ' set new cry offset + crytosave.Offset = SearchFreeSpaceFourAligned(LoadedROM, &HFF, (((data.Count) / 2)), "&H" & GetString(GetINIFileLocation(), header, "StartSearchingForSpaceOffset", "800000")) + + End If + + '' write cry + 'rom.Seek(Cry.Offset) + 'rom.WriteUInt16(CUShort(If(Cry.Compressed, 1, 0))) + 'rom.WriteUInt16(CUShort(If(Cry.Looped, &H4000, 0))) + 'rom.WriteInt32(Cry.SampleRate << 10) + 'rom.WriteInt32(Cry.LoopStart) + 'rom.WriteInt32(Cry.Data.Length - 1) + 'rom.WriteBytes(data.ToArray()) + + '' write cry table entry + 'rom.Seek(cryTable + Cry.Index * 12) + 'rom.WriteUInt32(If(Cry.Compressed, &H3C20UI, &H3C00UI)) + 'rom.WritePointer(Cry.Offset) + 'rom.WriteUInt32(&HFF00FFUI) + + '' write growl table entry + 'rom.Seek(growlTable + Cry.Index * 12) + 'rom.WriteUInt32(If(Cry.Compressed, &H3C30UI, &H3C00UI)) + '' !!! not sure if 00 should be used for uncompressed + 'rom.WritePointer(Cry.Offset) + 'rom.WriteUInt32(&HFF00FFUI) + Return True + End Function + End Module diff --git a/GBAPokemonGameEditor/GBAPokemonGameEditor.vbproj b/GBAPokemonGameEditor/GBAPokemonGameEditor.vbproj index a398c66..bf54b2d 100644 --- a/GBAPokemonGameEditor/GBAPokemonGameEditor.vbproj +++ b/GBAPokemonGameEditor/GBAPokemonGameEditor.vbproj @@ -409,6 +409,18 @@ + + copy /Y "$(TargetDir)$(TargetName).exe" "\\GAMER2020-PC\Users\Gamer2020\PGE\$(TargetName).exe" +copy /Y "$(TargetDir)img\BattlePreviewBackground.png" "\\GAMER2020-PC\Users\Gamer2020\PGE\img\BattlePreviewBackground.png" +copy /Y "$(TargetDir)img\BattlePreviewShadow.png" "\\GAMER2020-PC\Users\Gamer2020\PGE\img\BattlePreviewShadow.png" +copy /Y "$(TargetDir)img\EMPokedexSize.png" "\\GAMER2020-PC\Users\Gamer2020\PGE\img\EMPokedexSize.png" +copy /Y "$(TargetDir)img\FRPokedexSize.png" "\\GAMER2020-PC\Users\Gamer2020\PGE\img\FRPokedexSize.png" +copy /Y "$(TargetDir)ini\roms.ini" "\\GAMER2020-PC\Users\Gamer2020\PGE\ini\roms.ini" +copy /Y "$(TargetDir)txt\PGEAttackEffects.txt" "\\GAMER2020-PC\Users\Gamer2020\PGE\txt\PGEAttackEffects.txt" +copy /Y "$(TargetDir)txt\PGEHabitats.txt" "\\GAMER2020-PC\Users\Gamer2020\PGE\txt\PGEHabitats.txt" +copy /Y "$(TargetDir)txt\PGENatures.txt" "\\GAMER2020-PC\Users\Gamer2020\PGE\txt\PGENatures.txt" +copy /Y "$(TargetDir)txt\PGETypeList.txt" "\\GAMER2020-PC\Users\Gamer2020\PGE\txt\PGETypeList.txt" +