Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/nes-containers.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2022-11-06 00:27:35 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2022-11-06 00:33:44 +0300
commitd288c8a7bbfabe1d36006d3832543cdfc204421b (patch)
treedde69b20d5b7465313a9e475bca14c7106a6f8d4
parent0e1467af3728811f98952aaeea39a8039f090df3 (diff)
Some improvements
-rw-r--r--NesContainers.csproj6
-rw-r--r--NesFile.cs69
-rw-r--r--UnifFile.cs111
3 files changed, 95 insertions, 91 deletions
diff --git a/NesContainers.csproj b/NesContainers.csproj
index 5104559..94c63be 100644
--- a/NesContainers.csproj
+++ b/NesContainers.csproj
@@ -16,12 +16,12 @@
<PackageProjectUrl>https://github.com/ClusterM/nes-containers</PackageProjectUrl>
<Copyright>Alexey 'Cluster' Avdyukhin, 2022</Copyright>
<Description>A simple .NET Standard 2.0 library for reading and modifying NES/Famicom ROM containers: .nes (iNES, NES 2.0), .unf (UNIF), and .fds (Famicom Disk System images).</Description>
- <Version>1.0.0</Version>
+ <Version>1.0.1</Version>
<RepositoryUrl>https://github.com/ClusterM/nes-containers</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>nes,famicom,famicom disk system</PackageTags>
- <AssemblyVersion>1.0.0</AssemblyVersion>
- <FileVersion>1.0.0</FileVersion>
+ <AssemblyVersion>1.0.1</AssemblyVersion>
+ <FileVersion>1.0.1</FileVersion>
<Authors>Alexey 'Cluster' Avdyukhin</Authors>
</PropertyGroup>
diff --git a/NesFile.cs b/NesFile.cs
index 3877027..de87ced 100644
--- a/NesFile.cs
+++ b/NesFile.cs
@@ -14,39 +14,39 @@ namespace com.clusterrr.Famicom.Containers
/// <summary>
/// PRG data
/// </summary>
- public IEnumerable<byte> PRG
+ public byte[] PRG
{
- get => Array.AsReadOnly(prg);
+ get => prg;
set => prg = (value ?? new byte[0]).ToArray();
}
/// <summary>
- /// CHR data (can be null if none)
+ /// CHR data
/// </summary>
- public IEnumerable<byte>? CHR
+ public byte[] CHR
{
- get => chr.Length == 0 ? null : Array.AsReadOnly(chr);
+ get => chr;
set => chr = (value ?? new byte[0]).ToArray();
}
/// <summary>
- /// Trainer (can be null if none)
+ /// Trainer
/// </summary>
- public IEnumerable<byte>? Trainer
+ public byte[] Trainer
{
- get => trainer.Length == 0 ? null : Array.AsReadOnly(trainer);
+ get => trainer;
set
{
- if (value != null && value.Count() != 0 && value.Count() != 512)
- throw new ArgumentOutOfRangeException("Trainer size must be 512 bytes");
- chr = (value ?? new byte[0]).ToArray();
+ if (value != null && value.Count() != 0 && value.Count() > 512)
+ throw new ArgumentOutOfRangeException("Trainer size must be 512 bytes or less");
+ chr = value ?? new byte[0];
}
}
/// <summary>
- /// Miscellaneous ROM (NES 2.0 only, can be null if none)
+ /// Miscellaneous ROM (NES 2.0 only)
/// </summary>
- public IEnumerable<byte>? MiscellaneousROM
+ public byte[] MiscellaneousROM
{
- get => miscellaneousROM.Length == 0 ? null : Array.AsReadOnly(miscellaneousROM);
- set => miscellaneousROM = (value ?? new byte[0]).ToArray();
+ get => miscellaneousROM;
+ set => miscellaneousROM = value ?? new byte[0];
}
/// <summary>
/// Mapper number
@@ -635,7 +635,7 @@ namespace com.clusterrr.Famicom.Containers
}
uint offset = (uint)header.Length;
- if (trainer != null && trainer.Length > 0)
+ if (trainer.Length > 0)
{
if (offset < data.Length)
Array.Copy(data, offset, trainer, 0, Math.Max(0, Math.Min(trainer.Length, data.Length - offset)));
@@ -698,9 +698,6 @@ namespace com.clusterrr.Famicom.Containers
header[1] = (byte)'E';
header[2] = (byte)'S';
header[3] = 0x1A;
- if (prg == null) prg = new byte[0];
- if (chr == null) chr = new byte[0];
- if (trainer == null) trainer = new byte[0];
ulong prgSizePadded, chrSizePadded;
if (Version == iNesVersion.iNES)
{
@@ -750,7 +747,10 @@ namespace com.clusterrr.Famicom.Containers
data.AddRange(header);
if (trainer.Length > 0)
+ {
data.AddRange(trainer);
+ if (trainer.Length < 512) data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)512 - trainer.Length));
+ }
data.AddRange(prg);
data.AddRange(chr);
}
@@ -843,19 +843,22 @@ namespace com.clusterrr.Famicom.Containers
header[15] |= (byte)((byte)DefaultExpansionDevice & 0x3F);
data.AddRange(header);
- if (Trainer != null)
- data.AddRange(Trainer);
+ if (trainer.Length > 0)
+ {
+ data.AddRange(trainer);
+ if (trainer.Length < 512) data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)512 - trainer.Length));
+ }
data.AddRange(prg);
data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)prgSizePadded - prg.Length));
data.AddRange(chr);
data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)chrSizePadded - chr.Length));
- if (MiscellaneousROMsCount > 0 || (MiscellaneousROM != null && MiscellaneousROM.Count() > 0))
+ if (MiscellaneousROMsCount > 0 || miscellaneousROM.Length > 0)
{
if (MiscellaneousROMsCount == 0)
throw new InvalidDataException("MiscellaneousROMsCount is zero while MiscellaneousROM is not empty");
- if (MiscellaneousROM == null)
+ if (MiscellaneousROM.Length == 0)
throw new InvalidDataException("MiscellaneousROM is empty while MiscellaneousROMsCount is not zero");
- data.AddRange(MiscellaneousROM);
+ data.AddRange(miscellaneousROM);
}
}
return data.ToArray();
@@ -923,28 +926,18 @@ namespace com.clusterrr.Famicom.Containers
/// <summary>
/// Calculate MD5 checksum of ROM (CHR+PRG without header)
/// </summary>
+ /// <returns>MD5 checksum for all PRG and CHR data</returns>
public byte[] CalculateMD5()
{
var md5 = MD5.Create();
- if (prg == null) prg = new byte[0];
- if (chr == null) chr = new byte[0];
- var alldata = new byte[prg.Length + chr.Length];
- Array.Copy(prg, 0, alldata, 0, prg.Length);
- Array.Copy(chr, 0, alldata, prg.Length, chr.Count());
+ var alldata = Enumerable.Concat(prg ?? new byte[0], chr ?? new byte[0]).ToArray();
return md5.ComputeHash(alldata);
}
/// <summary>
/// Calculate CRC32 checksum of ROM (CHR+PRG without header)
/// </summary>
- public uint CalculateCRC32()
- {
- if (prg == null) prg = new byte[0];
- if (chr == null) chr = new byte[0];
- var alldata = new byte[prg.Length + chr.Length];
- Array.Copy(prg, 0, alldata, 0, prg.Length);
- Array.Copy(chr, 0, alldata, prg.Length, chr.Length);
- return Crc32Calculator.CalculateCRC32(alldata);
- }
+ /// <returns>CRC32 checksum for all PRG and CHR data</returns>
+ public uint CalculateCRC32() => Crc32Calculator.CalculateCRC32(Enumerable.Concat(prg ?? new byte[0], chr ?? new byte[0]).ToArray());
}
}
diff --git a/UnifFile.cs b/UnifFile.cs
index 6872372..7caf4ed 100644
--- a/UnifFile.cs
+++ b/UnifFile.cs
@@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
+using System.Runtime.ConstrainedExecution;
+using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;
@@ -12,7 +14,7 @@ namespace com.clusterrr.Famicom.Containers
/// <summary>
/// UNIF file container for NES/Famicom games
/// </summary>
- public class UnifFile : IEnumerable<KeyValuePair<string, IEnumerable<byte>>>
+ public class UnifFile : IEnumerable<KeyValuePair<string, byte[]>>
{
/// <summary>
/// UNIF fields
@@ -29,13 +31,13 @@ namespace com.clusterrr.Famicom.Containers
/// </summary>
/// <param name="key">UNIF data block key</param>
/// <returns></returns>
- public IEnumerable<byte> this[string key]
+ public byte[] this[string key]
{
get
{
if (key.Length != 4) throw new ArgumentException("UNIF data block key must be 4 characters long");
if (!ContainsField(key)) throw new IndexOutOfRangeException($"There is no {key} field");
- return Array.AsReadOnly(fields[key]);
+ return fields[key];
}
set
{
@@ -43,7 +45,7 @@ namespace com.clusterrr.Famicom.Containers
if (value == null)
this.RemoveField(key);
else
- fields[key] = (value ?? new byte[0]).ToArray();
+ fields[key] = value;
}
}
@@ -64,8 +66,8 @@ namespace com.clusterrr.Famicom.Containers
/// Returns enumerator that iterates throught fields
/// </summary>
/// <returns>IEnumerable object</returns>
- public IEnumerator<KeyValuePair<string, IEnumerable<byte>>> GetEnumerator()
- => fields.Select(kv => new KeyValuePair<string, IEnumerable<byte>>(kv.Key, Array.AsReadOnly(kv.Value))).GetEnumerator();
+ public IEnumerator<KeyValuePair<string, byte[]>> GetEnumerator()
+ => fields.Select(kv => new KeyValuePair<string, byte[]>(kv.Key, kv.Value)).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
@@ -145,10 +147,10 @@ namespace com.clusterrr.Famicom.Containers
foreach (var kv in this)
{
data.AddRange(Encoding.UTF8.GetBytes(kv.Key));
- var v = kv.Value.ToArray();
- int len = v.Length;
+ var value = kv.Value;
+ int len = value.Length;
data.AddRange(BitConverter.GetBytes(len));
- data.AddRange(v);
+ data.AddRange(value);
}
return data.ToArray();
}
@@ -188,11 +190,11 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// Mapper name
+ /// Mapper name (null if none)
/// </summary>
public string? Mapper
{
- get => ContainsField("MAPR") ? UTF8NToString(this["MAPR"].ToArray()) : null;
+ get => ContainsField("MAPR") ? UTF8NToString(this["MAPR"]) : null;
set
{
if (value == null)
@@ -203,7 +205,7 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// The dumper name
+ /// The dumper name (null if none)
/// </summary>
///
public string? DumperName
@@ -212,7 +214,7 @@ namespace com.clusterrr.Famicom.Containers
{
if (!ContainsField("DINF"))
return null;
- var data = this["DINF"].ToArray();
+ var data = this["DINF"];
if (data.Length >= 204 && data[0] != 0)
return UTF8NToString(data, 100);
else
@@ -224,7 +226,7 @@ namespace com.clusterrr.Famicom.Containers
{
if (!ContainsField("DINF"))
this["DINF"] = new byte[204];
- var data = this["DINF"].ToArray();
+ var data = this["DINF"];
for (int i = 0; i < 100; i++)
data[i] = 0;
if (value != null)
@@ -239,7 +241,7 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// The name of the dumping software or mechanism
+ /// The name of the dumping software or mechanism (null if none)
/// </summary>
public string? DumpingSoftware
{
@@ -247,7 +249,7 @@ namespace com.clusterrr.Famicom.Containers
{
if (!ContainsField("DINF"))
return null;
- var data = this["DINF"].ToArray();
+ var data = this["DINF"];
if (data.Length >= 204 && data[0] != 0)
return UTF8NToString(data, 100, 104);
else
@@ -259,7 +261,7 @@ namespace com.clusterrr.Famicom.Containers
{
if (!ContainsField("DINF"))
this["DINF"] = new byte[204];
- var data = this["DINF"].ToArray();
+ var data = this["DINF"];
for (int i = 104; i < 104 + 100; i++)
data[i] = 0;
if (value != null)
@@ -274,14 +276,14 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// Date of the dump
+ /// Date of the dump (null if none)
/// </summary>
public DateTime? DumpDate
{
get
{
if (!ContainsField("DINF")) return null;
- var data = this["DINF"].ToArray();
+ var data = this["DINF"];
if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 0)
return null;
return new DateTime(
@@ -296,7 +298,7 @@ namespace com.clusterrr.Famicom.Containers
{
if (!ContainsField("DINF"))
this["DINF"] = new byte[204];
- var data = this["DINF"].ToArray();
+ var data = this["DINF"];
if (value != null)
{
data[100] = (byte)value.Value.Day;
@@ -307,10 +309,7 @@ namespace com.clusterrr.Famicom.Containers
else
{
// Is it valid?
- data[100] = 0;
- data[101] = 0;
- data[102] = 0;
- data[103] = 0;
+ data[100] = data[101] = data[102] = data[103] = 0;
}
this["DINF"] = data;
}
@@ -319,11 +318,11 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// Name of the game
+ /// Name of the game (null if none)
/// </summary>
public string? GameName
{
- get => ContainsField("NAME") ? UTF8NToString(this["NAME"].ToArray()) : null;
+ get => ContainsField("NAME") ? UTF8NToString(this["NAME"]) : null;
set
{
if (value == null)
@@ -334,7 +333,7 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// For non-homebrew NES/Famicom games, this field's value is always a function of the region in which a game was released
+ /// For non-homebrew NES/Famicom games, this field's value is always a function of the region in which a game was released (null if none)
/// </summary>
public Timing? Region
{
@@ -355,7 +354,7 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// Controllers usable by this game (bitmask)
+ /// Controllers usable by this game, bitmask (null if none)
/// </summary>
public Controller? Controllers
{
@@ -376,7 +375,7 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// Battery-backed (or other non-volatile memory) memory is present
+ /// Battery-backed (or other non-volatile memory) memory is present (null if none)
/// </summary>
public bool? Battery
{
@@ -397,7 +396,7 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// Mirroring type
+ /// Mirroring type (null if none)
/// </summary>
public MirroringType? Mirroring
{
@@ -418,9 +417,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// PRG0 field
+ /// PRG0 field (null if none)
/// </summary>
- public IEnumerable<byte>? PRG0
+ public byte[]? PRG0
{
get
{
@@ -439,9 +438,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// PRG1 field
+ /// PRG1 field (null if none)
/// </summary>
- public IEnumerable<byte>? PRG1
+ public byte[]? PRG1
{
get
{
@@ -460,9 +459,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// PRG2 field
+ /// PRG2 field (null if none)
/// </summary>
- public IEnumerable<byte>? PRG2
+ public byte[]? PRG2
{
get
{
@@ -481,9 +480,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// PRG3 field
+ /// PRG3 field (null if none)
/// </summary>
- public IEnumerable<byte>? PRG3
+ public byte[]? PRG3
{
get
{
@@ -502,9 +501,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// CHR0 field
+ /// CHR0 field (null if none)
/// </summary>
- public IEnumerable<byte>? CHR0
+ public byte[]? CHR0
{
get
{
@@ -523,9 +522,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// CHR1 field
+ /// CHR1 field (null if none)
/// </summary>
- public IEnumerable<byte>? CHR1
+ public byte[]? CHR1
{
get
{
@@ -544,9 +543,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// CHR2 field
+ /// CHR2 field (null if none)
/// </summary>
- public IEnumerable<byte>? CHR2
+ public byte[]? CHR2
{
get
{
@@ -565,9 +564,9 @@ namespace com.clusterrr.Famicom.Containers
}
/// <summary>
- /// CHR3 field
+ /// CHR3 field (null if none)
/// </summary>
- public IEnumerable<byte>? CHR3
+ public byte[]? CHR3
{
get
{
@@ -593,21 +592,33 @@ namespace com.clusterrr.Famicom.Containers
foreach (var kv in this.Where(kv => kv.Key.StartsWith("PRG")))
{
var num = kv.Key[3];
- var crc32 = Crc32Calculator.CalculateCRC32(kv.Value.ToArray());
+ var crc32 = Crc32Calculator.CalculateCRC32(kv.Value);
this[$"PCK{num}"] = BitConverter.GetBytes(crc32);
}
foreach (var kv in this.Where(kv => kv.Key.StartsWith("CHR")))
{
var num = kv.Key[3];
- var crc32 = Crc32Calculator.CalculateCRC32(kv.Value.ToArray());
+ var crc32 = Crc32Calculator.CalculateCRC32(kv.Value);
this[$"CCK{num}"] = BitConverter.GetBytes(crc32);
}
}
/// <summary>
- /// Calculate overall CRC32
+ /// Calculate MD5 checksum of ROM (all PRG fields + all CHR fields)
/// </summary>
- /// <returns></returns>
+ /// <returns>MD5 checksum for all PRG and CHR data</returns>
+ public byte[] CalculateMD5()
+ {
+ var md5 = MD5.Create();
+ var alldata = Enumerable.Concat(fields.Where(k => k.Key.StartsWith("PRG")).OrderBy(k => k.Key).SelectMany(i => i.Value),
+ fields.Where(k => k.Key.StartsWith("CHR")).OrderBy(k => k.Key).SelectMany(i => i.Value)).ToArray();
+ return md5.ComputeHash(alldata);
+ }
+
+ /// <summary>
+ /// Calculate CRC32 checksum of ROM (all PRG fields + all CHR fields)
+ /// </summary>
+ /// <returns>CRC32 checksum for all PRG and CHR data</returns>
public uint CalculateCRC32()
=> Crc32Calculator.CalculateCRC32(
Enumerable.Concat(fields.Where(k => k.Key.StartsWith("PRG")).OrderBy(k => k.Key).SelectMany(i => i.Value),