diff --git a/MarioPartyCompression/MarioPartyCompression.cs b/MarioPartyCompression/MarioPartyCompression.cs
index edefae9..de8afb9 100644
--- a/MarioPartyCompression/MarioPartyCompression.cs
+++ b/MarioPartyCompression/MarioPartyCompression.cs
@@ -17,6 +17,16 @@ public static class MPCompression
/// Path where the uncompressed data will be saved.
public static void Decompress(string inputFilePath, string outputFilePath)
{
+ if (string.IsNullOrEmpty(inputFilePath))
+ {
+ throw new ArgumentNullException("inputFilePath");
+ }
+
+ if (string.IsNullOrEmpty(outputFilePath))
+ {
+ throw new ArgumentNullException("outputFilePath");
+ }
+
if (!File.Exists(inputFilePath))
{
throw new FileNotFoundException($"File \"{inputFilePath}\" does not exist!", inputFilePath);
@@ -36,6 +46,11 @@ public static void Decompress(string inputFilePath, string outputFilePath)
/// The uncompressed data buffer.
public static byte[] Decompress(byte[] compressedBuffer)
{
+ if (compressedBuffer == null)
+ {
+ throw new ArgumentNullException("compressedBuffer");
+ }
+
using (MemoryStream compressedStream = new MemoryStream(compressedBuffer))
{
return Decompress(compressedStream);
@@ -49,6 +64,11 @@ public static byte[] Decompress(byte[] compressedBuffer)
/// The uncompressed data buffer.
public static byte[] Decompress(Stream compressedStream)
{
+ if (compressedStream == null)
+ {
+ throw new ArgumentNullException("compressedStream");
+ }
+
byte[] uncompressedBuffer;
using (BinaryReader br = new BinaryReader(compressedStream, Encoding.UTF8, true))
@@ -141,6 +161,16 @@ public static byte[] Decompress(Stream compressedStream)
/// Path where the compressed data will be saved.
public static void Compress(string inputFilePath, string outputFilePath)
{
+ if (string.IsNullOrEmpty(inputFilePath))
+ {
+ throw new ArgumentNullException("inputFilePath");
+ }
+
+ if (string.IsNullOrEmpty(outputFilePath))
+ {
+ throw new ArgumentNullException("outputFilePath");
+ }
+
if (!File.Exists(inputFilePath))
{
throw new FileNotFoundException($"File \"{inputFilePath}\" does not exist!", inputFilePath);
@@ -160,6 +190,11 @@ public static void Compress(string inputFilePath, string outputFilePath)
/// The compressed data buffer.
public static byte[] Compress(byte[] uncompressedBuffer)
{
+ if (uncompressedBuffer == null)
+ {
+ throw new ArgumentNullException("uncompressedBuffer");
+ }
+
using (MemoryStream compressedStream = new MemoryStream(uncompressedBuffer))
{
return Compress(compressedStream);
@@ -173,6 +208,11 @@ public static byte[] Compress(byte[] uncompressedBuffer)
/// The compressed data buffer.
public static byte[] Compress(Stream uncompressedStream)
{
+ if (uncompressedStream == null)
+ {
+ throw new ArgumentNullException("uncompressedStream");
+ }
+
// Create a buffer big enough to contain the search window (SEARCH_WINDOW_SIZE) and the uncompressed data
byte[] uncompressedBuffer = new byte[uncompressedStream.Length + SEARCH_WINDOW_SIZE];
uint uncompressedBufferPosition = SEARCH_WINDOW_SIZE;
diff --git a/MarioPartyCompression/MarioPartyCompression.csproj b/MarioPartyCompression/MarioPartyCompression.csproj
index b815b28..334a445 100644
--- a/MarioPartyCompression/MarioPartyCompression.csproj
+++ b/MarioPartyCompression/MarioPartyCompression.csproj
@@ -5,9 +5,9 @@
PacoChan
Mario Party Compression Library
A Mario Party compression library.
- 1.0.0.0
- 1.0.0.0
- 1.0.0
+ 1.0.1.0
+ 1.0.1.0
+ 1.0.1
https://github.com/MaikelChan/MarioPartyCompression
diff --git a/MarioPartyTools/Benchmark.cs b/MarioPartyTools/Benchmark.cs
index d170298..bdc0279 100644
--- a/MarioPartyTools/Benchmark.cs
+++ b/MarioPartyTools/Benchmark.cs
@@ -7,10 +7,33 @@ namespace MarioPartyTools
{
static class Benchmark
{
- public enum RomRegions { NtscU, Pal, NtscJ }
+ static readonly byte[][] RomCRCs =
+ {
+ new byte[] { 0xad, 0xa8, 0x15, 0xbe, 0x60, 0x28, 0x62, 0x2f }, // NTSC-J
+ new byte[] { 0x28, 0x29, 0x65, 0x7e, 0xa0, 0x62, 0x18, 0x77 }, // NTSC-U
+ new byte[] { 0x9c, 0x66, 0x30, 0x69, 0x80, 0xf2, 0x4a, 0x80 } // PAL
+ };
+
+ static readonly RomData[] romData =
+ {
+ new RomData() { startPosition = 0x31ba80, endPosition = 0xfb47a0 }, // NTSC-J
+ new RomData() { startPosition = 0x31c7e0, endPosition = 0xfcb860 }, // NTSC-U
+ new RomData() { startPosition = 0x3373c0, endPosition = 0xff0850 } // PAL
+ };
+
+ struct RomData
+ {
+ public uint startPosition;
+ public uint endPosition;
+ }
- public static void Start(string romFilePath, RomRegions romRegion)
+ public static void Start(string romFilePath)
{
+ if (string.IsNullOrEmpty(romFilePath))
+ {
+ throw new ArgumentNullException("romFilePath");
+ }
+
if (!File.Exists(romFilePath))
{
throw new FileNotFoundException($"File \"{romFilePath}\" does not exist!", romFilePath);
@@ -19,31 +42,43 @@ public static void Start(string romFilePath, RomRegions romRegion)
float compressionRatio = 0f;
int processedFiles = 0;
Stopwatch sw = new Stopwatch();
- sw.Start();
-
- uint dataStartPosition;
- uint dataEndPosition;
-
- if (romRegion == RomRegions.NtscU)
- {
- dataStartPosition = 0x31c7e0;
- dataEndPosition = 0xfcb860;
- }
- else if (romRegion == RomRegions.Pal)
- {
- dataStartPosition = 0x3373c0;
- dataEndPosition = 0xff0850;
- }
- else
- {
- dataStartPosition = 0x31ba80;
- dataEndPosition = 0xfb47a0;
- }
using (FileStream romStream = File.OpenRead(romFilePath))
using (BinaryReader br = new BinaryReader(romStream))
{
- romStream.Seek(dataStartPosition, SeekOrigin.Begin);
+ // Check if it is a valid ROM
+
+ romStream.Seek(0x10, SeekOrigin.Begin);
+ byte[] romCRC = new byte[0x8];
+ romStream.Read(romCRC, 0, romCRC.Length);
+ byte[] swappedRomCRC = SwapCRCBytes(romCRC);
+
+ int romRegion = -1;
+
+ for (int r = 0; r < RomCRCs.Length; r++)
+ {
+ if (CompareArrays(RomCRCs[r], swappedRomCRC))
+ {
+ throw new FormatException("The Mario Party ROM has swapped data. Please unswap it using another tool first.");
+ }
+
+ if (CompareArrays(RomCRCs[r], romCRC))
+ {
+ romRegion = r;
+ break;
+ }
+ }
+
+ if (romRegion < 0)
+ {
+ throw new FormatException("The file is not a Mario Party ROM for N64. Please make sure you've specified the correct game.");
+ }
+
+ // Start reading the ROM data
+
+ sw.Start();
+
+ romStream.Seek(romData[romRegion].startPosition, SeekOrigin.Begin);
uint fileCount = SwapU32(br.ReadUInt32());
uint[] filePointers = new uint[fileCount];
@@ -55,7 +90,7 @@ public static void Start(string romFilePath, RomRegions romRegion)
for (int f = 0; f < fileCount; f++)
{
- romStream.Seek(filePointers[f] + dataStartPosition, SeekOrigin.Begin);
+ romStream.Seek(filePointers[f] + romData[romRegion].startPosition, SeekOrigin.Begin);
uint subFileCount = SwapU32(br.ReadUInt32());
uint[] subFilePointers = new uint[subFileCount];
@@ -76,12 +111,12 @@ public static void Start(string romFilePath, RomRegions romRegion)
else
{
if (f < fileCount - 1)
- subFileLength = (filePointers[f + 1] + dataStartPosition) - (filePointers[f] + subFilePointers[sf] + dataStartPosition);
+ subFileLength = (filePointers[f + 1] + romData[romRegion].startPosition) - (filePointers[f] + subFilePointers[sf] + romData[romRegion].startPosition);
else
- subFileLength = dataEndPosition - (filePointers[f] + subFilePointers[sf] + dataStartPosition);
+ subFileLength = romData[romRegion].endPosition - (filePointers[f] + subFilePointers[sf] + romData[romRegion].startPosition);
}
- uint subFilePosition = filePointers[f] + subFilePointers[sf] + dataStartPosition;
+ uint subFilePosition = filePointers[f] + subFilePointers[sf] + romData[romRegion].startPosition;
romStream.Seek(subFilePosition, SeekOrigin.Begin);
byte[] data = new byte[subFileLength];
romStream.Read(data, 0, data.Length);
@@ -101,7 +136,7 @@ public static void Start(string romFilePath, RomRegions romRegion)
compressionRatio /= processedFiles;
Console.ForegroundColor = ConsoleColor.Green;
- Console.WriteLine($"\nDone! Compression ratio: {compressionRatio}. Total time: {sw.ElapsedMilliseconds}ms.\n");
+ Console.WriteLine($"\nProcess finished! Compression ratio average: {compressionRatio}. Elapsed time: {sw.ElapsedMilliseconds}ms.\n");
}
static bool ProcessFile(byte[] compressedBuffer, uint offset, out float compressionRatio)
@@ -109,7 +144,7 @@ static bool ProcessFile(byte[] compressedBuffer, uint offset, out float compress
compressionRatio = 0f;
Console.ForegroundColor = ConsoleColor.Gray;
- Console.WriteLine($"Processing data at 0x{offset:X8}...");
+ Console.Write($"Processing data at 0x{offset:X8}... ");
byte[] uncompressedBuffer;
@@ -120,7 +155,7 @@ static bool ProcessFile(byte[] compressedBuffer, uint offset, out float compress
catch (FormatException ex)
{
Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine($"Error processing data at 0x{offset:X8}: {ex.Message}");
+ Console.WriteLine(ex.Message);
return false;
}
@@ -131,11 +166,14 @@ static bool ProcessFile(byte[] compressedBuffer, uint offset, out float compress
if (!areEqual)
{
Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine($"Data at 0x{offset:X8} decompression mismatch...");
+ Console.WriteLine("Decompression mismatch.");
return false;
}
- compressionRatio = (float)compressedBuffer.Length / compressedBuffer2.Length;
+ compressionRatio = (float)compressedBuffer2.Length / compressedBuffer.Length;
+
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine($"Done! Compression ratio: {compressionRatio}");
return true;
}
@@ -152,6 +190,24 @@ static bool CompareArrays(byte[] arr1, byte[] arr2)
return true;
}
+ static byte[] SwapCRCBytes(byte[] bytes)
+ {
+ if (bytes.Length != 8)
+ {
+ throw new ArgumentException("The bytes array must have 8 elements.", "bytes");
+ }
+
+ byte[] swappedBytes = new byte[8];
+
+ for (int b = 0; b < bytes.Length; b += 2)
+ {
+ swappedBytes[b] = bytes[b + 1];
+ swappedBytes[b + 1] = bytes[b];
+ }
+
+ return swappedBytes;
+ }
+
static uint SwapU32(uint value)
{
return (value << 24) | ((value & 0x0000FF00) << 8) | ((value & 0x00FF0000) >> 8) | (value >> 24);
diff --git a/MarioPartyTools/Program.cs b/MarioPartyTools/Program.cs
index d007e6a..ee3f5f8 100644
--- a/MarioPartyTools/Program.cs
+++ b/MarioPartyTools/Program.cs
@@ -10,7 +10,26 @@ static void Main(string[] args)
{
ShowHeader();
- if (args.Length == 3)
+ if (args.Length == 2)
+ {
+ if (args[0] == "-b")
+ {
+ try
+ {
+ Benchmark.Start(args[1]);
+ }
+ catch (Exception ex)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(ex.Message);
+ }
+ }
+ else
+ {
+ ShowUsage();
+ }
+ }
+ else if (args.Length == 3)
{
if (args[0] == "-d")
{
@@ -42,25 +61,6 @@ static void Main(string[] args)
Console.WriteLine(ex.Message);
}
}
- else if (args[0] == "-b")
- {
- if (args[1] == "-rU")
- {
- Benchmark.Start(args[2], Benchmark.RomRegions.NtscU);
- }
- else if (args[1] == "-rP")
- {
- Benchmark.Start(args[2], Benchmark.RomRegions.Pal);
- }
- else if (args[1] == "-rJ")
- {
- Benchmark.Start(args[2], Benchmark.RomRegions.NtscJ);
- }
- else
- {
- ShowUsage();
- }
- }
else
{
ShowUsage();
@@ -106,7 +106,7 @@ static void ShowUsage()
Console.WriteLine(" MarioPartyTools -d : Decompress a Mario Party file");
Console.WriteLine(" MarioPartyTools -c : Compress a Mario Party file");
- Console.WriteLine(" MarioPartyTools -b -r : Execute some benchmarks and tests\n");
+ Console.WriteLine(" MarioPartyTools -b : Execute some benchmarks and tests\n");
Console.ForegroundColor = ConsoleColor.Gray;
@@ -114,10 +114,6 @@ static void ShowUsage()
Console.WriteLine(" in a Mario Party ROM, show the compression ratio compared to the original data,");
Console.WriteLine(" check if there are compression errors, and show how much it took to do all that.");
Console.WriteLine(" Also make sure that the ROM is not swapped, or else the process will fail.\n");
-
- Console.WriteLine(" rom_region: the benchmark is compatible with all versions of Mario Party.");
- Console.WriteLine(" Use -rP to specify the PAL version, -rU for the NTSC-U version,");
- Console.WriteLine(" and -rJ for the NTSC-J version.\n");
}
}
}
\ No newline at end of file
diff --git a/MarioPartyTools/Properties/AssemblyInfo.cs b/MarioPartyTools/Properties/AssemblyInfo.cs
index 5d2266e..02547df 100644
--- a/MarioPartyTools/Properties/AssemblyInfo.cs
+++ b/MarioPartyTools/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("1.0.1.0")]
+[assembly: AssemblyFileVersion("1.0.1.0")]
diff --git a/README.md b/README.md
index c3c6fd3..1621bba 100644
--- a/README.md
+++ b/README.md
@@ -18,18 +18,19 @@ The program requires the .NET Framework 4.7.2.
```
MarioPartyTools -d : Decompress a Mario Party file
MarioPartyTools -c : Compress a Mario Party file
-MarioPartyTools -b -r : Execute some benchmarks and tests
+MarioPartyTools -b : Execute some benchmarks and tests
The benchmark command will try to decompress and recompress all compressed data
in a Mario Party ROM, show the compression ratio compared to the original data,
check if there are compression errors, and show how much it took to do all that.
Also make sure that the ROM is not swapped, or else the process will fail.
-
- rom_region: the benchmark is compatible with all versions of Mario Party.
- Use -rP to specify the PAL version, -rU for the NTSC-U version,
- and -rJ for the NTSC-J version.
```
# Changelog
+### [1.0.1] - 2020-08-28
+- Added some safety checks and error messages.
+- Benchmark auto detects the region of the game.
+- Benchmark improvements in the way data is shown in the console.
+
### [1.0.0] - 2020-08-28
- Initial release.
\ No newline at end of file