From ee11aae7f78810c4e002e26b03754a46d0a18ddd Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Fri, 4 Nov 2022 16:53:02 +0400 Subject: Nullable types --- FdsBlockDiskInfo.cs | 10 ++-- FdsBlockFileAmount.cs | 2 +- FdsBlockFileData.cs | 22 +------ FdsBlockFileHeader.cs | 4 +- FdsDiskFile.cs | 8 +-- NesContainers.csproj | 3 +- NesFile.cs | 12 ++-- UnifFile.cs | 159 ++++++++++++++++++++++++-------------------------- 8 files changed, 99 insertions(+), 121 deletions(-) diff --git a/FdsBlockDiskInfo.cs b/FdsBlockDiskInfo.cs index 9f91957..5a893ef 100644 --- a/FdsBlockDiskInfo.cs +++ b/FdsBlockDiskInfo.cs @@ -653,7 +653,7 @@ namespace com.clusterrr.Famicom.Containers public Company LicenseeCode { get => (Company)manufacturerCode; set => manufacturerCode = (byte)value; } [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - byte[] gameName; + byte[] gameName = Encoding.ASCII.GetBytes("---"); /// /// 3-letter ASCII code per game (e.g. ZEL for The Legend of Zelda) /// @@ -718,7 +718,7 @@ namespace com.clusterrr.Famicom.Containers readonly byte unknown06 = 0xFF; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - byte[] manufacturingDate; + byte[] manufacturingDate = { 0, 0, 0 }; /// /// Manufacturing date /// @@ -769,10 +769,10 @@ namespace com.clusterrr.Famicom.Containers readonly ushort unknown09 = 0x0200; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] // Speculative: some kind of game information representation? - readonly byte[] unknown10; + readonly byte[] unknown10 = { 0, 0, 0, 0, 0 }; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - byte[] rewrittenDate; + byte[] rewrittenDate = { 0, 0, 0 }; /// /// "Rewritten disk" date. It's speculated this refers to the date the disk was formatted and rewritten by something like a Disk Writer kiosk. /// In the case of an original (non-copied) disk, this should be the same as Manufacturing date @@ -881,7 +881,7 @@ namespace com.clusterrr.Famicom.Containers } /// - /// Return raw data + /// Returns raw data /// /// Data public byte[] ToBytes() diff --git a/FdsBlockFileAmount.cs b/FdsBlockFileAmount.cs index 868b22e..54be4cf 100644 --- a/FdsBlockFileAmount.cs +++ b/FdsBlockFileAmount.cs @@ -48,7 +48,7 @@ namespace com.clusterrr.Famicom.Containers } /// - /// Return raw data + /// Returns raw data /// /// Data public byte[] ToBytes() => new byte[] { blockType, fileAmount }; diff --git a/FdsBlockFileData.cs b/FdsBlockFileData.cs index 8ec31b4..9ce46f5 100644 --- a/FdsBlockFileData.cs +++ b/FdsBlockFileData.cs @@ -29,16 +29,6 @@ namespace com.clusterrr.Famicom.Containers set => data = value.ToArray(); } - /// - /// Set by dumper. True when checksum is ok - /// - public bool CrcOk { get; set; } = true; - - /// - /// Set by dumper. True when "end of head" flag was meet during dumping - /// - public bool EndOfHeadMeet { get; set; } = false; - /// /// Length of the block /// @@ -63,18 +53,10 @@ namespace com.clusterrr.Famicom.Containers } /// - /// Return raw data + /// Returns raw data /// /// Data - public byte[] ToBytes() - { - var result = new List - { - blockType - }; - result.AddRange(Data); - return result.ToArray(); - } + public byte[] ToBytes() => Enumerable.Concat(new[] { blockType }, data).ToArray(); /// /// String representation diff --git a/FdsBlockFileHeader.cs b/FdsBlockFileHeader.cs index ee0233c..a884a87 100644 --- a/FdsBlockFileHeader.cs +++ b/FdsBlockFileHeader.cs @@ -57,7 +57,7 @@ namespace com.clusterrr.Famicom.Containers public byte FileIndicateCode { get => fileIndicateCode; set => fileIndicateCode = value; } [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - private byte[] fileName; + private byte[] fileName = Encoding.ASCII.GetBytes("FILENAME"); /// /// Filename /// @@ -122,7 +122,7 @@ namespace com.clusterrr.Famicom.Containers } /// - /// Return raw data + /// Returns raw data /// /// Data public byte[] ToBytes() diff --git a/FdsDiskFile.cs b/FdsDiskFile.cs index c3c2e53..88e3dce 100644 --- a/FdsDiskFile.cs +++ b/FdsDiskFile.cs @@ -62,8 +62,8 @@ namespace com.clusterrr.Famicom.Containers /// File data block public FdsDiskFile(FdsBlockFileHeader headerBlock, FdsBlockFileData dataBlock) { - this.HeaderBlock = headerBlock; - this.DataBlock = dataBlock; + this.headerBlock = headerBlock; + this.dataBlock = dataBlock; headerBlock.FileSize = (ushort)dataBlock.Data.Count(); } @@ -72,8 +72,8 @@ namespace com.clusterrr.Famicom.Containers /// public FdsDiskFile() { - this.HeaderBlock = new FdsBlockFileHeader(); - this.DataBlock = new FdsBlockFileData(); + this.headerBlock = new FdsBlockFileHeader(); + this.dataBlock = new FdsBlockFileData(); HeaderBlock.FileSize = (ushort)DataBlock.Data.Count(); } diff --git a/NesContainers.csproj b/NesContainers.csproj index b8ea4c0..fbc4df3 100644 --- a/NesContainers.csproj +++ b/NesContainers.csproj @@ -4,12 +4,13 @@ netstandard2.0 NesContainers com.clusterrr.Famicom.Containers - 7.3 + 11 embedded True NesContainers.xml LICENSE README.md + enable diff --git a/NesFile.cs b/NesFile.cs index 06dc837..042b7ec 100644 --- a/NesFile.cs +++ b/NesFile.cs @@ -22,17 +22,17 @@ namespace com.clusterrr.Famicom.Containers /// /// CHR data (can be null if none) /// - public IEnumerable CHR + public IEnumerable? CHR { - get => Array.AsReadOnly(chr); + get => chr.Length == 0 ? null : Array.AsReadOnly(chr); set => chr = (value ?? new byte[0]).ToArray(); } /// /// Trainer (can be null if none) /// - public IEnumerable Trainer + public IEnumerable? Trainer { - get => Array.AsReadOnly(trainer); + get => trainer.Length == 0 ? null : Array.AsReadOnly(trainer); set { if (value != null && value.Count() != 0 && value.Count() != 512) @@ -43,9 +43,9 @@ namespace com.clusterrr.Famicom.Containers /// /// Miscellaneous ROM (NES 2.0 only, can be null if none) /// - public IEnumerable MiscellaneousROM + public IEnumerable? MiscellaneousROM { - get => Array.AsReadOnly(miscellaneousROM); + get => miscellaneousROM.Length == 0 ? null : Array.AsReadOnly(miscellaneousROM); set => miscellaneousROM = (value ?? new byte[0]).ToArray(); } /// diff --git a/UnifFile.cs b/UnifFile.cs index 921edec..e1d9bad 100644 --- a/UnifFile.cs +++ b/UnifFile.cs @@ -5,6 +5,7 @@ using System.Drawing; using System.IO; using System.Linq; using System.Text; +using System.Xml.Linq; namespace com.clusterrr.Famicom.Containers { @@ -16,7 +17,7 @@ namespace com.clusterrr.Famicom.Containers /// /// UNIF fields /// - private Dictionary fields = new Dictionary(); + private Dictionary fields = new Dictionary(); /// /// UNIF version @@ -28,11 +29,12 @@ namespace com.clusterrr.Famicom.Containers /// /// UNIF data block key /// - public IEnumerable this[string key] + public IEnumerable? this[string key] { get { if (key.Length != 4) throw new ArgumentException("UNIF data block key must be 4 characters long"); + if (!fields.ContainsKey(key)) return null; return Array.AsReadOnly(fields[key]); } set @@ -82,7 +84,7 @@ namespace com.clusterrr.Famicom.Containers pos += 4; var fieldData = new byte[length]; Array.Copy(data, pos, fieldData, 0, length); - fields[type] = fieldData; + this[type] = fieldData; pos += length; } } @@ -124,15 +126,16 @@ namespace com.clusterrr.Famicom.Containers header[7] = (byte)((Version >> 24) & 0xFF); data.AddRange(header); - foreach (var name in fields.Keys) + foreach (var kv in this) { - data.AddRange(Encoding.UTF8.GetBytes(name)); - int len = fields[name].Length; + data.AddRange(Encoding.UTF8.GetBytes(kv.Key)); + var v = kv.Value.ToArray(); + int len = v.Length; data.Add((byte)(len & 0xFF)); data.Add((byte)((len >> 8) & 0xFF)); data.Add((byte)((len >> 16) & 0xFF)); data.Add((byte)((len >> 24) & 0xFF)); - data.AddRange(fields[name]); + data.AddRange(v); } return data.ToArray(); } @@ -148,8 +151,9 @@ namespace com.clusterrr.Famicom.Containers /// /// Input text /// Output byte[] array - private static byte[] StringToUTF8N(string text) + private static byte[]? StringToUTF8N(string? text) { + if (text == null) return null; var str = Encoding.UTF8.GetBytes(text); var result = new byte[str.Length + 1]; Array.Copy(str, result, str.Length); @@ -163,8 +167,9 @@ namespace com.clusterrr.Famicom.Containers /// Maximum number of bytes to parse /// Start offset /// - private static string UTF8NToString(byte[] data, int maxLength = int.MaxValue, int offset = 0) + private static string? UTF8NToString(byte[]? data, int maxLength = int.MaxValue, int offset = 0) { + if (data == null || data.Length == 0) return null; int length = 0; while ((data[length + offset] != 0) && (length + offset < data.Length) && (length + offset < maxLength)) length++; @@ -174,108 +179,97 @@ namespace com.clusterrr.Famicom.Containers /// /// Mapper name /// - public string Mapper + public string? Mapper { - get - { - if (fields.ContainsKey("MAPR")) - return UTF8NToString(fields["MAPR"]); - else - return null; - } - set - { - fields["MAPR"] = StringToUTF8N(value); - } + get => UTF8NToString(fields["MAPR"]); + set => fields["MAPR"] = value == null ? null : StringToUTF8N(value); } /// /// The dumper name /// /// - public string DumperName + public string? DumperName { - get - { - if (!fields.ContainsKey("DINF")) - return null; - return UTF8NToString(fields["DINF"], 100); - } + get => UTF8NToString(fields["DINF"], 100); set { - if (!fields.ContainsKey("DINF")) - fields["DINF"] = new byte[204]; + if (this["DINF"] == null) + this["DINF"] = new byte[204]; + var data = this["DINF"].ToArray(); for (int i = 0; i < 100; i++) - fields["DINF"][i] = 0; - var name = StringToUTF8N(value); - Array.Copy(name, 0, fields["DINF"], 0, Math.Min(100, name.Length)); + data[i] = 0; + if (value != null) + { + var name = StringToUTF8N(value); + Array.Copy(name, 0, data, 0, Math.Min(100, name!.Length)); + } + this["DINF"] = data; } } /// /// The name of the dumping software or mechanism /// - public string DumpingSoftware + public string? DumpingSoftware { - get - { - if (!fields.ContainsKey("DINF")) - return null; - return UTF8NToString(fields["DINF"], 100, 104); - } + get => UTF8NToString(fields["DINF"], 100, 104); set { - if (!fields.ContainsKey("DINF")) - fields["DINF"] = new byte[204]; + if (this["DINF"] == null) + this["DINF"] = new byte[204]; + var data = this["DINF"].ToArray(); for (int i = 104; i < 104 + 100; i++) - fields["DINF"][i] = 0; - var name = StringToUTF8N(value); - Array.Copy(name, 0, fields["DINF"], 104, Math.Min(100, name.Length)); + data[i] = 0; + if (value != null) + { + var name = StringToUTF8N(value); + Array.Copy(name, 0, fields["DINF"], 104, Math.Min(100, name!.Length)); + } + this["DINF"] = data; } } /// /// Date of the dump /// - public DateTime DumpDate + public DateTime? DumpDate { get { + var data = this["DINF"]?.ToArray(); + if (data == null) return null; if (!fields.ContainsKey("DINF")) return new DateTime(); return new DateTime( - year: fields["DINF"][102] | (fields["DINF"][103] << 8), - month: fields["DINF"][101], - day: fields["DINF"][100] + year: data[102] | (data[103] << 8), + month: data[101], + day: data[100] ); } set { - if (!fields.ContainsKey("DINF")) - fields["DINF"] = new byte[204]; - fields["DINF"][100] = (byte)value.Day; - fields["DINF"][101] = (byte)value.Month; - fields["DINF"][102] = (byte)(value.Year & 0xFF); - fields["DINF"][103] = (byte)(value.Year >> 8); + if (this["DINF"] == null) + this["DINF"] = new byte[204]; + if (value != null) + { + var data = this["DINF"].ToArray(); + data[100] = (byte)value.Value.Day; + data[101] = (byte)value.Value.Month; + data[102] = (byte)(value.Value.Year & 0xFF); + data[103] = (byte)(value.Value.Year >> 8); + this["DINF"] = data; + } } } /// /// Name of the game /// - public string GameName + public string? GameName { - get - { - if (fields.ContainsKey("NAME")) - return UTF8NToString(fields["NAME"]); - else - return null; - } - set - { - fields["NAME"] = StringToUTF8N(value); - } + get => UTF8NToString(this["NAME"]?.ToArray()); + set => this["NAME"] = StringToUTF8N(value); } /// @@ -285,14 +279,15 @@ namespace com.clusterrr.Famicom.Containers { get { - if (fields.ContainsKey("TVCI") && fields["TVCI"].Length > 0) - return (NesFile.Timing)fields["TVCI"][0]; + var data = this["TVCI"]; + if (data != null && data.Any()) + return (NesFile.Timing)data.First(); else return NesFile.Timing.Ntsc; } set { - fields["TVCI"] = new byte[] { (byte)value }; + this["TVCI"] = new byte[] { (byte)value }; } } @@ -303,8 +298,8 @@ namespace com.clusterrr.Famicom.Containers { get { - if (fields.ContainsKey("CTRL") && fields["CTRL"].Length > 0) - return (Controller)fields["CTRL"][0]; + if (this["CTRL"]?.Any() == true) + return (Controller)this["CTRL"].First(); else return Controller.None; } @@ -321,8 +316,8 @@ namespace com.clusterrr.Famicom.Containers { get { - if (fields.ContainsKey("BATR") && fields["BATR"].Length > 0) - return fields["BATR"][0] != 0; + if (this["BATR"]?.Any() == true) + return this["BATR"].First() != 0; else return false; } @@ -339,8 +334,8 @@ namespace com.clusterrr.Famicom.Containers { get { - if (fields.ContainsKey("MIRR") && fields["MIRR"].Length > 0) - return (MirroringType)fields["MIRR"][0]; + if (this["MIRR"]?.Any() == true) + return (MirroringType)fields["MIRR"].First(); else return MirroringType.Unknown; } @@ -355,10 +350,10 @@ namespace com.clusterrr.Famicom.Containers /// public void CalculateAndStoreCRCs() { - foreach (var key in fields.Keys.Where(k => k.StartsWith("PRG"))) + foreach (var kv in this.Where(kv => kv.Key.StartsWith("PRG"))) { - var num = key[3]; - var crc32 = Crc32Calculator.CalculateCRC32(fields[key]); + var num = kv.Key[3]; + var crc32 = Crc32Calculator.CalculateCRC32(kv.Value.ToArray()); fields[$"PCK{num}"] = new byte[] { (byte)(crc32 & 0xFF), (byte)((crc32 >> 8) & 0xFF), @@ -366,10 +361,10 @@ namespace com.clusterrr.Famicom.Containers (byte)((crc32 >> 24) & 0xFF) }; } - foreach (var key in fields.Keys.Where(k => k.StartsWith("CHR"))) + foreach (var kv in this.Where(kv => kv.Key.StartsWith("CHR"))) { - var num = key[3]; - var crc32 = Crc32Calculator.CalculateCRC32(fields[key]); + var num = kv.Key[3]; + var crc32 = Crc32Calculator.CalculateCRC32(kv.Value.ToArray()); fields[$"CCK{num}"] = new byte[] { (byte)(crc32 & 0xFF), (byte)((crc32 >> 8) & 0xFF), -- cgit v1.2.3