Advertisement
Guest User

SNES ROM reader. By Elektro

a guest
Jun 11th, 2015
571
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
VB.NET 36.47 KB | None | 0 0
  1. ' ***********************************************************************
  2. ' Author   : Elektro.
  3. '            Based on this 3rd party project:
  4. '            https://github.com/Zeokat/SNES-ROM-Header-Dumper-CSharp/blob/master/snes_dumper.cs
  5. ' Modified : 11-June-2015
  6. ' ***********************************************************************
  7. ' <copyright file="SnesRom.vb" company="Elektro Studios">
  8. '     Copyright (c) Elektro Studios. All rights reserved.
  9. ' </copyright>
  10. ' ***********************************************************************
  11.  
  12. #Region " Usage Examples "
  13.  
  14. #Region " Read SNES ROM "
  15.  
  16. 'Dim romDump As New SnesRom("C:\ROM.smc")
  17. '' Or...
  18. '' Dim romDump As New SnesRom(File.ReadAllBytes("C:\ROM.smc"))
  19.  
  20. 'Dim sb As New StringBuilder
  21. 'With sb
  22. '    .AppendLine(String.Format("Name..................: {0}", romDump.Name))
  23. '    .AppendLine(String.Format("Bank Type.............: {0}", romDump.BankType.ToString))
  24. '    .AppendLine(String.Format("Cartridge Type........: {0}", romDump.CartridgeType.ToString.ToUpper.Replace("_", "/")))
  25. '    .AppendLine(String.Format("Checksum..............: {0}", String.Format("0x{0}", Convert.ToString(CInt(romDump.Checksum), toBase:=16).ToUpper)))
  26. '    .AppendLine(String.Format("Checksum Complement...: {0}", String.Format("0x{0}", Convert.ToString(CInt(romDump.ChecksumComplement), toBase:=16).ToUpper)))
  27. '    .AppendLine(String.Format("Country Code..........: {0}", romDump.Country.Code.ToString))
  28. '    .AppendLine(String.Format("Country Name..........: {0}", romDump.Country.Name))
  29. '    .AppendLine(String.Format("Country Region........: {0}", romDump.Country.Region.ToString.ToUpper))
  30. '    .AppendLine(String.Format("Header Type (SMC).....: {0}", romDump.HeaderType.ToString))
  31. '    .AppendLine(String.Format("Layout................: {0}", romDump.Layout.ToString))
  32. '    .AppendLine(String.Format("License Code/Name.....: {0}", romDump.LicenseCode.ToString))
  33. '    .AppendLine(String.Format("ROM Size..............: {0} MBits", romDump.RomSize.ToString.Substring(1, romDump.RomSize.ToString.LastIndexOf("M") - 2)).Replace("_or_", "/"))
  34. '    .AppendLine(String.Format("RAM Size..............: {0} KBits", romDump.RamSize.ToString.Substring(1, romDump.RamSize.ToString.LastIndexOf("K") - 2)))
  35. '    .AppendLine(String.Format("Version Number........: 1.{0}", romDump.Version.ToString))
  36. 'End With
  37.  
  38. 'Clipboard.SetText(sb.ToString) : MessageBox.Show(sb.ToString)
  39.  
  40. #End Region
  41.  
  42. #Region " Modify SNES ROM "
  43.  
  44. 'Dim romDump As New SnesRom("C:\ROM.smc")
  45. '' Or...
  46. '' Dim romDump As New SnesRom(File.ReadAllBytes("C:\ROM.smc"))
  47.  
  48. 'With romDump
  49. '    .Name = "Elektrocitos"
  50. '    .Version = 1
  51. '    .Country = New SnesRom.CountryData(countryCode:=0) ' Japan.
  52. '    .LicenseCode = SnesRom.LicenseCodeEnum.Taito
  53. '    .RomSize = SnesRom.ROMSizeEnum._4_Mbits
  54. '    .RamSize = SnesRom.SRAMSizeEnum._256_Kbits
  55. '    .Save("C:\Elektron.smc", replace:=True)
  56. 'End With
  57.  
  58. #End Region
  59.  
  60. #End Region
  61.  
  62. #Region " Option Statements "
  63.  
  64. Option Strict On
  65. Option Explicit On
  66. Option Infer Off
  67.  
  68. #End Region
  69.  
  70. #Region " Imports "
  71.  
  72. Imports System.IO
  73. Imports System.Text
  74.  
  75. #End Region
  76.  
  77. ''' <summary>
  78. ''' Read or modify a SNES ROM header.
  79. ''' </summary>
  80. Public NotInheritable Class SnesRom
  81.  
  82. #Region " Properties "
  83.  
  84.     ''' <summary>
  85.     ''' Gets the raw byte-data of the ROM file.
  86.     ''' </summary>
  87.     ''' <value>The raw byte-data of the ROM file.</value>
  88.     Public ReadOnly Property RawData As Byte()
  89.         Get
  90.             Return Me.rawDataB
  91.         End Get
  92.     End Property
  93.     ''' <summary>
  94.     ''' (backing field) The raw byte-data of the ROM file.
  95.     ''' </summary>
  96.     Private ReadOnly rawDataB As Byte()
  97.  
  98.     ''' <summary>
  99.     ''' Gets The ROM header type.
  100.     ''' </summary>
  101.     ''' <remarks>http://romhack.wikia.com/wiki/SMC_header</remarks>
  102.     ''' <value>The ROM header type.</value>
  103.     Public ReadOnly Property HeaderType As HeaderTypeEnum
  104.         Get
  105.             Return Me.headerTypeB
  106.         End Get
  107.     End Property
  108.     ''' <summary>
  109.     ''' (backing field) The ROM header type.
  110.     ''' </summary>
  111.     Private headerTypeB As HeaderTypeEnum
  112.  
  113.     ''' <summary>
  114.     ''' Gets the SNES header address location.
  115.     ''' </summary>
  116.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  117.     ''' <value>The SNES header address location.</value>
  118.     Private ReadOnly Property HeaderLocation As Integer
  119.         Get
  120.             Return Me.headerLocationB
  121.         End Get
  122.     End Property
  123.     ''' <summary>
  124.     ''' (backing field) The SNES header address location.
  125.     ''' </summary>
  126.     Private headerLocationB As Integer = 33216
  127.  
  128.     ''' <summary>
  129.     ''' Gets or sets the name of the ROM, typically in ASCII.
  130.     ''' The name buffer consists in 21 characters.
  131.     ''' </summary>
  132.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  133.     ''' <value>The name of the ROM.</value>
  134.     Public Property Name As String
  135.         Get
  136.             Return Me.nameB
  137.         End Get
  138.         Set(ByVal value As String)
  139.             Me.SetName(value)
  140.             Me.nameB = value
  141.         End Set
  142.     End Property
  143.     ''' <summary>
  144.     ''' (backing field) The name of the ROM.
  145.     ''' </summary>
  146.     Private nameB As String
  147.  
  148.     ''' <summary>
  149.     ''' Gets the ROM layout.
  150.     ''' The SNES ROM layout describes how the ROM banks appear in a ROM image and in the SNES address space.
  151.     ''' </summary>
  152.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_ROM_layout</remarks>
  153.     ''' <value>The ROM layout.</value>
  154.     Public ReadOnly Property Layout As Byte
  155.         Get
  156.             Return Me.layoutB
  157.         End Get
  158.     End Property
  159.     ''' <summary>
  160.     ''' (backing field) The ROM layout.
  161.     ''' </summary>
  162.     Private layoutB As Byte
  163.  
  164.     ''' <summary>
  165.     ''' Gets the bank type.
  166.     ''' An image contains only LoROM banks or only HiROM banks, not both.
  167.     ''' </summary>
  168.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_ROM_layout</remarks>
  169.     ''' <value>The bank type.</value>
  170.     Public ReadOnly Property BankType As BankTypeEnum
  171.         Get
  172.             Return Me.bankTypeB
  173.         End Get
  174.     End Property
  175.     ''' <summary>
  176.     ''' (backing field) The bank type.
  177.     ''' </summary>
  178.     Private bankTypeB As BankTypeEnum
  179.  
  180.     ''' <summary>
  181.     ''' Gets the cartrifge type, it can be a ROM only, or a ROM with save-RAM.
  182.     ''' </summary>
  183.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  184.     ''' <value>The cartridge type.</value>
  185.     Public ReadOnly Property CartridgeType As CartridgeTypeEnum
  186.         Get
  187.             Return Me.cartridgeTypeB
  188.         End Get
  189.     End Property
  190.     ''' <summary>
  191.     ''' (backing field) The cartrifge type.
  192.     ''' </summary>
  193.     Private cartridgeTypeB As CartridgeTypeEnum
  194.  
  195.     ''' <summary>
  196.     ''' Gets or sets the ROM size, in megabits.
  197.     ''' </summary>
  198.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  199.     ''' <value>The ROM size, in megabits.</value>
  200.     Public Property RomSize As ROMSizeEnum
  201.         Get
  202.             Return DirectCast(Me.romSizeB, ROMSizeEnum)
  203.         End Get
  204.         Set(ByVal value As ROMSizeEnum)
  205.             Me.SetByte(Me.startAddressRomSize, value)
  206.             Me.romSizeB = value
  207.         End Set
  208.     End Property
  209.     ''' <summary>
  210.     ''' (backing field) The ROM size-byte (from 8 to 13).
  211.     ''' </summary>
  212.     Private romSizeB As Byte
  213.  
  214.     ''' <summary>
  215.     ''' Gets or sets the RAM size, in kilobits.
  216.     ''' If value is 0, the ROM has no RAM.
  217.     ''' </summary>
  218.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  219.     ''' <value>The RAM size, in kilobits.</value>
  220.     Public Property RamSize As SRAMSizeEnum
  221.         Get
  222.             ' Formula:
  223.             ' 1 << (3 + Me.ramSizeB) = "X" KBits
  224.             Return DirectCast(Me.ramSizeB, SRAMSizeEnum)
  225.         End Get
  226.         Set(ByVal value As SRAMSizeEnum)
  227.             Me.SetByte(Me.startAddressRamSize, value)
  228.             Me.ramSizeB = value
  229.         End Set
  230.     End Property
  231.     ''' <summary>
  232.     ''' (backing field) The RAM size-byte (from 0 to 5).
  233.     ''' </summary>
  234.     Private ramSizeB As Byte
  235.  
  236.     ''' <summary>
  237.     ''' Gets or sets the country data.
  238.     ''' </summary>
  239.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  240.     ''' <value>The country data.</value>
  241.     Public Property Country As CountryData
  242.         Get
  243.             Return New CountryData(Me.CountryCode)
  244.         End Get
  245.         Set(ByVal value As CountryData)
  246.             Me.SetByte(Me.startAddressCountryCode, value.Code)
  247.             Me.CountryCode = value.Code
  248.         End Set
  249.     End Property
  250.  
  251.     ''' <summary>
  252.     ''' The country code.
  253.     ''' </summary>
  254.     Private Property CountryCode As Byte
  255.  
  256.     ''' <summary>
  257.     ''' Gets or sets the license code.
  258.     ''' </summary>
  259.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  260.     ''' <value>The license code.</value>
  261.     Public Property LicenseCode As LicenseCodeEnum
  262.         Get
  263.             Return DirectCast(Me.licenseCodeB, LicenseCodeEnum)
  264.         End Get
  265.         Set(ByVal value As LicenseCodeEnum)
  266.             Me.SetByte(Me.startAddressLicenseCode, value)
  267.             Me.licenseCodeB = value
  268.         End Set
  269.     End Property
  270.     ''' <summary>
  271.     ''' (backing field) The license code.
  272.     ''' </summary>
  273.     Private licenseCodeB As Byte
  274.  
  275.     ''' <summary>
  276.     ''' Gets or sets the version number.
  277.     ''' </summary>
  278.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  279.     ''' <value>The version number.</value>
  280.     Public Property Version As Byte
  281.         Get
  282.             Return Me.versionB
  283.         End Get
  284.         Set(ByVal value As Byte)
  285.             Me.SetByte(Me.startAddressVersion, Version)
  286.             Me.versionB = value
  287.         End Set
  288.     End Property
  289.     ''' <summary>
  290.     ''' (backing field) The version number.
  291.     ''' </summary>
  292.     Private versionB As Byte
  293.  
  294.     ''' <summary>
  295.     ''' Gets the checksum complement.
  296.     ''' </summary>
  297.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  298.     ''' <value>The checksum complement.</value>
  299.     Public ReadOnly Property ChecksumComplement As UShort
  300.         Get
  301.             Return Me.checksumComplementB
  302.         End Get
  303.     End Property
  304.     ''' <summary>
  305.     ''' (backing field) The checksum complement.
  306.     ''' </summary>
  307.     Private checksumComplementB As UShort
  308.  
  309.     ''' <summary>
  310.     ''' Gets the checksum.
  311.     ''' </summary>
  312.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  313.     ''' <value>The checksum.</value>
  314.     Public ReadOnly Property Checksum As UShort
  315.         Get
  316.             Return Me.checksumB
  317.         End Get
  318.     End Property
  319.     ''' <summary>
  320.     ''' (backing field) The checksum.
  321.     ''' </summary>
  322.     Private checksumB As UShort
  323.  
  324. #End Region
  325.  
  326. #Region " Header Addresses "
  327.  
  328.     ' ********************************************************************************************************************
  329.     ' NOTE:
  330.     ' The reason for the variables that are commented-out is just because are unused, but could be helpful in the future.
  331.     ' ********************************************************************************************************************
  332.  
  333.     ' ''' <summary>
  334.     ' ''' The start address of a Lo-ROM header.
  335.     ' ''' </summary>
  336.     'Private ReadOnly loRomHeaderAddress As UShort = 32704
  337.  
  338.     ' ''' <summary>
  339.     ' ''' The start address of a Hi-ROM header.
  340.     ' ''' </summary>
  341.     'Private ReadOnly hiRomHeaderAddress As UShort = 65472
  342.  
  343.     ''' <summary>
  344.     ''' The start address of the ROM name.
  345.     ''' </summary>
  346.     Private ReadOnly startAddressName As Integer = 0
  347.  
  348.     ''' <summary>
  349.     ''' The end address of the ROM name.
  350.     ''' </summary>
  351.     Private ReadOnly endAddressName As Integer = 20
  352.  
  353.     ''' <summary>
  354.     ''' The start address of the ROM layout.
  355.     ''' </summary>
  356.     Private ReadOnly startAddressLayout As Integer = 21
  357.  
  358.     ' ''' <summary>
  359.     ' ''' The end address of the ROM layout.
  360.     ' ''' </summary>
  361.     'Private ReadOnly endAddressLayout As Integer = 21
  362.  
  363.     ''' <summary>
  364.     ''' The start address of the ROM cartridge type.
  365.     ''' </summary>
  366.     Private ReadOnly startAddressCartridgeType As Integer = 22
  367.  
  368.     ' ''' <summary>
  369.     ' ''' The end address of the ROM cartridge type.
  370.     ' ''' </summary>
  371.     'Private ReadOnly endAddressCartridgeType As Integer = 22
  372.  
  373.     ''' <summary>
  374.     ''' The start address of the ROM size (rom).
  375.     ''' </summary>
  376.     Private ReadOnly startAddressRomSize As Integer = 23
  377.  
  378.     ' ''' <summary>
  379.     ' ''' The end address of the ROM size (rom).
  380.     ' ''' </summary>
  381.     'Private ReadOnly endAddressRomSize As Integer = 23
  382.  
  383.     ''' <summary>
  384.     ''' The start address of the ROM size (ram).
  385.     ''' </summary>
  386.     Private ReadOnly startAddressRamSize As Integer = 24
  387.  
  388.     ' ''' <summary>
  389.     ' ''' The end address of the ROM size (ram).
  390.     ' ''' </summary>
  391.     'Private ReadOnly endAddressRamSize As Integer = 24
  392.  
  393.     ''' <summary>
  394.     ''' The start address of the ROM country code.
  395.     ''' </summary>
  396.     Private ReadOnly startAddressCountryCode As Integer = 25
  397.  
  398.     ' ''' <summary>
  399.     ' ''' The end address of the ROM country code.
  400.     ' ''' </summary>
  401.     'Private ReadOnly endAddressCountryCode As Integer = 25
  402.  
  403.     ''' <summary>
  404.     ''' The start address of the ROM license code.
  405.     ''' </summary>
  406.     Private ReadOnly startAddressLicenseCode As Integer = 26
  407.  
  408.     ' ''' <summary>
  409.     ' ''' The end address of the ROM license code.
  410.     ' ''' </summary>
  411.     'Private ReadOnly endAddressLicenseCode As Integer = 26
  412.  
  413.     ''' <summary>
  414.     ''' The start address of the ROM Version Number.
  415.     ''' </summary>
  416.     Private ReadOnly startAddressVersion As Integer = 27
  417.  
  418.     ' ''' <summary>
  419.     ' ''' The end address of the ROM Version Number.
  420.     ' ''' </summary>
  421.     'Private ReadOnly endAddresVersion As Integer = 27
  422.  
  423.     ''' <summary>
  424.     ''' The start address of the ROM checksum complement.
  425.     ''' </summary>
  426.     Private ReadOnly startAddressChecksumComplement As Integer = 28
  427.  
  428.     ''' <summary>
  429.     ''' The end address of the ROM checksum complement.
  430.     ''' </summary>
  431.     Private ReadOnly endAddressChecksumComplement As Integer = 29
  432.  
  433.     ''' <summary>
  434.     ''' The start address of the ROM checksum.
  435.     ''' </summary>
  436.     Private ReadOnly startAddressChecksum As Integer = 30
  437.  
  438.     ''' <summary>
  439.     ''' The end address of the ROM checksum.
  440.     ''' </summary>
  441.     Private ReadOnly endAddressChecksum As Integer = 31
  442.  
  443. #End Region
  444.  
  445. #Region " Enumerations "
  446.  
  447.     ''' <summary>
  448.     ''' Specifies a SNES ROM header type.
  449.     ''' A headered ROM has SMC header and SNES header.
  450.     ''' A headerless ROM has no SMC header, but still contains a SNES header.
  451.     ''' Note that both a LoRom and HiRom images can be headered, or headerless.
  452.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  453.     ''' </summary>
  454.     Public Enum HeaderTypeEnum As Integer
  455.  
  456.         ''' <summary>
  457.         ''' A headered SNES ROM.
  458.         ''' The ROM contains an SMC header, and also contains an SNES header.
  459.         ''' </summary>
  460.         Headered = 0
  461.  
  462.         ''' <summary>
  463.         ''' A headerless SNES ROM.
  464.         ''' The ROM does not contains an SMC header, but contains an SNES header.
  465.         ''' </summary>
  466.         Headerless = 1
  467.  
  468.     End Enum
  469.  
  470.     ''' <summary>
  471.     ''' Specifies a SNES ROM bank type.
  472.     ''' <remarks>http://romhack.wikia.com/wiki/SNES_ROM_layout</remarks>
  473.     ''' </summary>
  474.     Public Enum BankTypeEnum As UShort
  475.  
  476.         ''' <summary>
  477.         ''' A LoROM maps each ROM bank into the upper half (being addresses $8000 to $ffff) of each SNES bank,
  478.         ''' starting with SNES bank $00, and starting again with SNES bank $80.
  479.         ''' </summary>
  480.         LoRom = 32704US
  481.  
  482.         ''' <summary>
  483.         ''' A HiROM maps each ROM bank into the whole (being addresses $0000 to $ffff) of each SNES bank,
  484.         ''' starting with SNES bank $40, and starting again with SNES bank $80.
  485.         ''' </summary>
  486.         HiRom = 65472US
  487.  
  488.     End Enum
  489.  
  490.     ''' <summary>
  491.     ''' Specifies a SNES ROM cartridge type.
  492.     ''' <remarks>http://softpixel.com/~cwright/sianse/docs/Snesrom.txt</remarks>
  493.     ''' </summary>
  494.     Public Enum CartridgeTypeEnum As Byte
  495.  
  496.         ''' <summary>
  497.         ''' A ROM only.
  498.         ''' </summary>
  499.         ROM = 0
  500.  
  501.         ''' <summary>
  502.         ''' A ROM with RAM.
  503.         ''' </summary>
  504.         ROM_RAM = 1
  505.  
  506.         ''' <summary>
  507.         ''' A ROM with Save-RAM.
  508.         ''' </summary>
  509.         ROM_SRAM = 2
  510.  
  511.         ''' <summary>
  512.         '''
  513.         ''' </summary>
  514.         ROM_DSP1 = 3
  515.  
  516.         ''' <summary>
  517.         '''
  518.         ''' </summary>
  519.         ROM_DSP1_RAM = 4
  520.  
  521.         ''' <summary>
  522.         '''
  523.         ''' </summary>
  524.         ROM_DSP1_SRAM = 5
  525.  
  526.         ''' <summary>
  527.         '''
  528.         ''' </summary>
  529.         ROM_FX = 19
  530.  
  531.         ''' <summary>
  532.         '''
  533.         ''' </summary>
  534.         ROM_RAM_GB = 227
  535.  
  536.         ''' <summary>
  537.         '''
  538.         ''' </summary>
  539.         ROM_DSP2 = 246
  540.  
  541.     End Enum
  542.  
  543.     ''' <summary>
  544.     ''' Specifies a SNES Save-RAM size.
  545.     ''' <remarks>http://softpixel.com/~cwright/sianse/docs/Snesrom.txt</remarks>
  546.     ''' </summary>
  547.     Public Enum ROMSizeEnum As Byte
  548.  
  549.         ''' <summary>
  550.         ''' ROM of 2 Megabit size.
  551.         ''' </summary>
  552.         _2_Mbits = 8
  553.  
  554.         ''' <summary>
  555.         ''' ROM of 4 Megabit size.
  556.         ''' </summary>
  557.         _4_Mbits = 9
  558.  
  559.         ''' <summary>
  560.         ''' ROM of 8 Megabit size.
  561.         ''' </summary>
  562.         _8_Mbits = 10
  563.  
  564.         ''' <summary>
  565.         ''' ROM of 10, 12, or 16 Megabit size.
  566.         ''' </summary>
  567.         _10_or_12_or_16_Mbits = 11
  568.  
  569.         ''' <summary>
  570.         ''' ROM of 24 or 32 Megabit size.
  571.         ''' </summary>
  572.         _24_or_32_Mbits = 12
  573.  
  574.         ''' <summary>
  575.         ''' ROM of 48 or 64 Megabit size.
  576.         ''' </summary>
  577.         _48_or_64_Mbits = 13
  578.  
  579.     End Enum
  580.  
  581.     ''' <summary>
  582.     ''' Specifies a SNES Save-RAM size.
  583.     ''' <remarks>http://softpixel.com/~cwright/sianse/docs/Snesrom.txt</remarks>
  584.     ''' </summary>
  585.     Public Enum SRAMSizeEnum As Byte
  586.  
  587.         ''' <summary>
  588.         ''' The ROM does not contains an S-RAM.
  589.         ''' </summary>
  590.         _0_Kbits = 0
  591.  
  592.         ''' <summary>
  593.         ''' S-RAM of 16 Kilobits.
  594.         ''' </summary>
  595.         _16_Kbits = 1
  596.  
  597.         ''' <summary>
  598.         ''' S-RAM of 32 Kilobits.
  599.         ''' </summary>
  600.         _32_Kbits = 2
  601.  
  602.         ''' <summary>
  603.         ''' S-RAM of 64 Kilobits.
  604.         ''' </summary>
  605.         _64_Kbits = 3
  606.  
  607.         ''' <summary>
  608.         ''' S-RAM of 128 Kilobits.
  609.         ''' </summary>
  610.         _128_Kbits = 4
  611.  
  612.         ''' <summary>
  613.         ''' S-RAM of 256 Kilobits.
  614.         ''' </summary>
  615.         _256_Kbits = 5
  616.  
  617.     End Enum
  618.  
  619.     ''' <summary>
  620.     ''' Specifies a SNES ROM license code.
  621.     ''' <remarks>http://softpixel.com/~cwright/sianse/docs/Snesrom.txt</remarks>
  622.     ''' </summary>
  623.     Public Enum LicenseCodeEnum As Byte
  624.  
  625.         ''' <summary>
  626.         ''' Any license company.
  627.         ''' </summary>
  628.         _NoLicense = 0
  629.  
  630.         Nintendo = 1
  631.         Zamuse = 5
  632.         Capcom = 8
  633.         Hot_B = 9
  634.         Jaleco = 10
  635.         Storm = 11
  636.         Mebio_Software = 15
  637.         Gremlin_Graphics = 18
  638.         COBRA_Team = 21
  639.         Human_Field = 22
  640.         Hudson_Soft = 24
  641.         Yanoman = 26
  642.         Tecmo = 28
  643.         Forum = 30
  644.         Virgin = 31
  645.         Tokai_Engeneering = 33
  646.         POW = 34
  647.         Loriciel = 35
  648.         Enix = 38
  649.         Kemco = 40
  650.         Seta = 41
  651.         Visit = 45
  652.         HECT = 53
  653.         Loriciel_1 = 61
  654.         Seika = 64
  655.         UBI_Soft = 65
  656.         Spectrum_Holobyte = 71
  657.         Irem = 73
  658.         Raya_Systems = 75
  659.         Renovation_Pruducts = 76
  660.         Malibu_Games = 77
  661.         US_Gold = 79
  662.         Absolute_Entertainment = 80
  663.         Acclaim = 81
  664.         Activision = 82
  665.         American_Sammy = 83
  666.         GameTek = 84
  667.         Hi_Tech = 85
  668.         LJN_Toys = 86
  669.         Mindscape = 90
  670.         Technos_Japan = 93
  671.         American_Softworks = 95
  672.         Titus = 96
  673.         Virgin_Games = 97
  674.         Maxis = 98
  675.         Ocean = 103
  676.         Electronic_Arts = 105
  677.         Laser_Beam = 107
  678.         Elite = 110
  679.         Electro_Brain = 111
  680.         Infogrames = 112
  681.         Interplay = 113
  682.         LucasArts = 114
  683.         Sculptured_Soft = 115
  684.         Storm_1 = 117
  685.         THQ_Software = 120
  686.         Accolade_Inc = 121
  687.         Triffix_Entertainment = 122
  688.         Microprose = 124
  689.         Kemco_1 = 127
  690.         Namco = 130
  691.         Koei = 132
  692.         Tokuma_Shoten_Intermedia = 134
  693.         DATAM_Polystar = 136
  694.         Bullet_Proof_Software = 139
  695.         Vic_Tokai = 140
  696.         IMax = 143
  697.         CHUN_Soft = 145
  698.         Video_System = 146
  699.         BEC = 147
  700.         Kaneco = 151
  701.         Pack_in_Video = 153
  702.         Nichibutsu = 154
  703.         Tecmo_1 = 155
  704.         Imagineer = 156
  705.         Wolf_Team = 160
  706.         Konami = 164
  707.         K_Amusement = 165
  708.         Takara = 167
  709.         Technos_Japan_1 = 169
  710.         JVC = 170
  711.         Toei_Animation = 172
  712.         Toho = 173
  713.         Namco_1 = 175
  714.         Activision_1 = 177
  715.         BanDai_America = 178
  716.         Enix_1 = 180
  717.         Halken = 182
  718.         Culture_Brain = 186
  719.         Sunsoft = 187
  720.         Toshiba_EMI = 188
  721.         Sony_Imagesoft = 189
  722.         Sammy = 191
  723.         Taito = 192
  724.         Kemco_2 = 194
  725.         Square = 195
  726.         NHK = 196
  727.         Data_East = 197
  728.         Tonkin_House = 198
  729.         Koei_1 = 200
  730.         Konami_USA = 202
  731.         Meldac = 205
  732.         PONY_CANYON = 206
  733.         Sotsu_Agency = 207
  734.         Sofel = 209
  735.         Quest_Corp = 210
  736.         Sigma = 211
  737.         Naxat = 214
  738.         Capcom_1 = 216
  739.         Banpresto = 217
  740.         Hiro = 219
  741.         NCS = 221
  742.         Human_Entertainment = 222
  743.         Ringler_Studios = 223
  744.         Jaleco_1 = 224
  745.         Sotsu_Agency_1 = 226
  746.         TandESoft = 228
  747.         EPOCH = 229
  748.         Athena = 231
  749.         Asmik = 232
  750.         Natsume = 233
  751.         King = 234
  752.         Atlus = 235
  753.         Sony_Music = 236
  754.         Psygnosis = 238
  755.         Beam_Software = 243
  756.         Tec_Magik = 244
  757.         Hudson_Soft_1 = 255
  758.  
  759.     End Enum
  760.  
  761. #End Region
  762.  
  763. #Region " Exceptions "
  764.  
  765.     ''' <summary>
  766.     ''' Exception that is thrown when a SNES ROM has an invalid format.
  767.     ''' </summary>
  768.     <Serializable>
  769.     Public NotInheritable Class InvalidRomFormatException : Inherits Exception
  770.  
  771.         ''' <summary>
  772.         ''' Initializes a new instance of the <see cref="InvalidROMFormatException"/> class.
  773.         ''' </summary>
  774.         Public Sub New()
  775.             MyBase.New("The SNES ROM image has an invalid format.")
  776.         End Sub
  777.  
  778.         ''' <summary>
  779.         ''' Initializes a new instance of the <see cref="InvalidROMFormatException"/> class.
  780.         ''' </summary>
  781.         ''' <param name="message">The message that describes the error.</param>
  782.         Public Sub New(ByVal message As String)
  783.             MyBase.New(message)
  784.         End Sub
  785.  
  786.         ''' <summary>
  787.         ''' Initializes a new instance of the <see cref="InvalidROMFormatException"/> class.
  788.         ''' </summary>
  789.         ''' <param name="message">The message that describes the error.</param>
  790.         ''' <param name="inner">The inner exception.</param>
  791.         Public Sub New(ByVal message As String, ByVal inner As Exception)
  792.             MyBase.New(message, inner)
  793.         End Sub
  794.  
  795.     End Class
  796.  
  797. #End Region
  798.  
  799. #Region " Types "
  800.  
  801.     ''' <summary>
  802.     ''' Defines a SNES ROM country.
  803.     ''' </summary>
  804.     <Serializable>
  805.     Public NotInheritable Class CountryData
  806.  
  807. #Region " Properties "
  808.  
  809.         ''' <summary>
  810.         ''' Gets the region, which can de PAL or NTSC.
  811.         ''' </summary>
  812.         ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  813.         ''' <value>The country code.</value>
  814.         Public ReadOnly Property Region As RegionTypeEnum
  815.             Get
  816.                 Return Me.regionB
  817.             End Get
  818.         End Property
  819.         ''' <summary>
  820.         ''' (backing field) The region, which can de PAL or NTSC.
  821.         ''' </summary>
  822.         Private ReadOnly regionB As RegionTypeEnum
  823.  
  824.         ''' <summary>
  825.         ''' Gets the country code.
  826.         ''' </summary>
  827.         ''' <value>The country code.</value>
  828.         Public ReadOnly Property Code As Byte
  829.             Get
  830.                 Return Me.codeB
  831.             End Get
  832.         End Property
  833.         ''' <summary>
  834.         ''' (backing field) The country code.
  835.         ''' </summary>
  836.         Private ReadOnly codeB As Byte
  837.  
  838.         ''' <summary>
  839.         ''' Gets the country name.
  840.         ''' </summary>
  841.         ''' <value>The country name.</value>
  842.         Public ReadOnly Property Name As String
  843.             Get
  844.                 Return Me.nameB
  845.             End Get
  846.         End Property
  847.         ''' <summary>
  848.         ''' (backing field) The country name.
  849.         ''' </summary>
  850.         Private ReadOnly nameB As String
  851.  
  852. #End Region
  853.  
  854. #Region " Enumerations "
  855.  
  856.         ''' <summary>
  857.         ''' Specifies a SNES ROM region type.
  858.         ''' <remarks>http://romhack.wikia.com/wiki/SNES_header</remarks>
  859.         ''' </summary>
  860.         Public Enum RegionTypeEnum As Integer
  861.  
  862.             ''' <summary>
  863.             ''' A PAL SNES ROM.
  864.             ''' </summary>
  865.             Pal = 0
  866.  
  867.             ''' <summary>
  868.             ''' An NTSC SNES ROM.
  869.             ''' </summary>
  870.             Ntsc = 1
  871.  
  872.         End Enum
  873.  
  874. #End Region
  875.  
  876. #Region " Countries "
  877.  
  878.         ''' <summary>
  879.         ''' The NTSC and PAL countries, based on country code from 0 to 13, so countrycode 0 = Japan, countrycode 1 = United States, and so on.
  880.         ''' <remarks>http://softpixel.com/~cwright/sianse/docs/Snesrom.txt</remarks>
  881.         ''' </summary>
  882.         Private ReadOnly countryDict As New Dictionary(Of Integer, String) From
  883.             {
  884.                 {0, "Japan"},
  885.                 {1, "United States"},
  886.                 {2, "Europe, Oceania, Asia"},
  887.                 {3, "Sweden"},
  888.                 {4, "Finland"},
  889.                 {5, "Denmark"},
  890.                 {6, "France"},
  891.                 {7, "Holland"},
  892.                 {8, "Spain"},
  893.                 {9, "Germany, Austria, Switz"},
  894.                 {10, "Italy"},
  895.                 {11, "Hong Kong, China"},
  896.                 {12, "Indonesia"},
  897.                 {13, "Korea"}
  898.             }
  899.  
  900. #End Region
  901.  
  902. #Region " Regions "
  903.  
  904.         ''' <summary>
  905.         ''' The country codes for NTSC region.
  906.         ''' <remarks>http://softpixel.com/~cwright/sianse/docs/Snesrom.txt</remarks>
  907.         ''' </summary>
  908.         Private ReadOnly ntscRegionCodes As Integer() =
  909.             {0, 1}
  910.  
  911.         ''' <summary>
  912.         ''' The country codes for PAL region.
  913.         ''' <remarks>http://softpixel.com/~cwright/sianse/docs/Snesrom.txt</remarks>
  914.         ''' </summary>
  915.         Private ReadOnly palRegionCodes As Integer() =
  916.             {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
  917.  
  918. #End Region
  919.  
  920. #Region " Constructors "
  921.  
  922.         ''' <summary>
  923.         ''' Initializes a new instance of the <see cref="CountryData"/> class.
  924.         ''' </summary>
  925.         ''' <param name="countryCode">The SNES ROM country code.</param>
  926.         ''' <exception cref="ArgumentException">Invalid country code.;countryCode</exception>
  927.         Public Sub New(ByVal countryCode As Byte)
  928.  
  929.             If Not (Me.ntscRegionCodes.Concat(Me.palRegionCodes)).Contains(countryCode) Then
  930.                 Throw New ArgumentException(message:="Invalid country code.", paramName:="countryCode")
  931.  
  932.             Else
  933.                 Me.codeB = countryCode
  934.                 Me.nameB = Me.countryDict(countryCode)
  935.  
  936.                 ' Determine region.
  937.                 If Me.ntscRegionCodes.Contains(countryCode) Then
  938.                     Me.regionB = RegionTypeEnum.Ntsc
  939.  
  940.                 ElseIf Me.palRegionCodes.Contains(countryCode) Then
  941.                     Me.regionB = RegionTypeEnum.Pal
  942.  
  943.                 End If
  944.  
  945.             End If
  946.  
  947.         End Sub
  948.  
  949.         ''' <summary>
  950.         ''' Prevents a default instance of the <see cref="CountryData"/> class from being created.
  951.         ''' </summary>
  952.         Private Sub New()
  953.         End Sub
  954.  
  955. #End Region
  956.  
  957.     End Class
  958.  
  959. #End Region
  960.  
  961. #Region " Constructors "
  962.  
  963.     ''' <summary>
  964.     ''' Prevents a default instance of the <see cref="SnesRom"/> class from being created.
  965.     ''' </summary>
  966.     Private Sub New()
  967.     End Sub
  968.  
  969.     ''' <summary>
  970.     ''' Initializes a new instance of the <see cref="SnesRom"/> class.
  971.     ''' </summary>
  972.     ''' <param name="romFilePath">The SNES ROM file path.</param>
  973.     Public Sub New(ByVal romFilePath As String)
  974.  
  975.         Me.New(File.ReadAllBytes(romFilePath))
  976.  
  977.     End Sub
  978.  
  979.     ''' <summary>
  980.     ''' Initializes a new instance of the <see cref="SnesRom"/> class.
  981.     ''' </summary>
  982.     ''' <param name="romData">The raw byte-data of the ROM file.</param>
  983.     Public Sub New(ByVal romData As Byte())
  984.  
  985.         Me.rawDataB = romData
  986.  
  987.         Me.VerifyRomFormat()
  988.         Me.VerifyBankType()
  989.         Me.ReadHeader()
  990.  
  991.     End Sub
  992.  
  993. #End Region
  994.  
  995. #Region " Private Methods "
  996.  
  997.     ''' <summary>
  998.     ''' Reads the ROM header to retrieve the header data.
  999.     ''' </summary>
  1000.     Private Sub ReadHeader()
  1001.  
  1002.         ' Read range of bytes.
  1003.         Me.nameB = Encoding.ASCII.GetString(Me.GetBytes(Me.startAddressName, Me.endAddressName)).Trim
  1004.  
  1005.         ' Read single bytes.
  1006.         Me.layoutB = Me.GetByte(Me.startAddressLayout)
  1007.         Me.cartridgeTypeB = DirectCast(Me.GetByte(Me.startAddressCartridgeType), CartridgeTypeEnum)
  1008.         Me.romSizeB = Me.GetByte(Me.startAddressRomSize)
  1009.         Me.ramSizeB = Me.GetByte(Me.startAddressRamSize)
  1010.         Me.CountryCode = Me.GetByte(Me.startAddressCountryCode)
  1011.         Me.licenseCodeB = Me.GetByte(Me.startAddressLicenseCode)
  1012.         Me.versionB = Me.GetByte(Me.startAddressVersion)
  1013.  
  1014.     End Sub
  1015.  
  1016.     ''' <summary>
  1017.     ''' Verifies the SNES ROM format.
  1018.     ''' </summary>
  1019.     ''' <exception cref="SnesRom.InvalidRomFormatException">The SNES ROM image has an invalid format.</exception>
  1020.     Private Sub VerifyRomFormat()
  1021.  
  1022.         If (Me.rawDataB.Length Mod 1024 = 512) Then
  1023.             Me.headerTypeB = HeaderTypeEnum.Headered
  1024.  
  1025.         ElseIf (Me.rawDataB.Length Mod 1024 = 0) Then
  1026.             Me.headerTypeB = HeaderTypeEnum.Headerless
  1027.  
  1028.         Else
  1029.             Throw New InvalidRomFormatException(message:="The SNES ROM image has an invalid format.")
  1030.  
  1031.         End If
  1032.  
  1033.     End Sub
  1034.  
  1035.     ''' <summary>
  1036.     ''' Verifies the SNES ROM bank type.
  1037.     ''' </summary>
  1038.     ''' <exception cref="Exception">Cannot recognize the bank type.</exception>
  1039.     Private Sub VerifyBankType()
  1040.  
  1041.         If Me.HeaderIsAt(BankTypeEnum.LoRom) Then
  1042.             Me.bankTypeB = BankTypeEnum.LoRom
  1043.  
  1044.         ElseIf Me.HeaderIsAt(BankTypeEnum.HiRom) Then
  1045.             Me.bankTypeB = BankTypeEnum.HiRom
  1046.  
  1047.         Else
  1048.             Throw New Exception(message:="Cannot recognize the bank type.")
  1049.  
  1050.         End If
  1051.  
  1052.     End Sub
  1053.  
  1054.     ''' <summary>
  1055.     ''' Verifies the checksum.
  1056.     ''' </summary>
  1057.     ''' <remarks>
  1058.     ''' Offset 0x07FC0 in a headerless LoROM image (LoROM rom sin smc header)
  1059.     ''' Offset 0x0FFC0 in a headerless HiROM image (HiROM rom sin smc header)
  1060.     ''' </remarks>
  1061.     ''' <returns><c>true</c> if checksum is ok, <c>false</c> otherwise.</returns>
  1062.     Private Function VerifyChecksum() As Boolean
  1063.  
  1064.         If Me.HeaderType = HeaderTypeEnum.Headered Then
  1065.             Me.headerLocationB += 512
  1066.         End If
  1067.  
  1068.         Me.checksumComplementB = BitConverter.ToUInt16(Me.GetBytes(Me.startAddressChecksumComplement, Me.endAddressChecksumComplement), startIndex:=0)
  1069.  
  1070.         Me.checksumB = BitConverter.ToUInt16(Me.GetBytes(Me.startAddressChecksum, Me.endAddressChecksum), startIndex:=0)
  1071.  
  1072.         Return CUShort(Me.Checksum Xor Me.ChecksumComplement).Equals(UShort.MaxValue)
  1073.  
  1074.     End Function
  1075.  
  1076.     ''' <summary>
  1077.     ''' Determines whether the ROM header is in the specified address.
  1078.     ''' </summary>
  1079.     ''' <param name="address">The address.</param>
  1080.     ''' <returns><c>true</c> if the ROM header is in the specified address, <c>false</c> otherwise.</returns>
  1081.     Private Function HeaderIsAt(ByVal address As UShort) As Boolean
  1082.  
  1083.         Me.headerLocationB = address
  1084.         Return Me.VerifyChecksum()
  1085.  
  1086.     End Function
  1087.  
  1088.     ''' <summary>
  1089.     ''' Gets the specified byte from the raw byte-data.
  1090.     ''' </summary>
  1091.     ''' <param name="address">The address.</param>
  1092.     ''' <returns>The specified byte from the raw byte-data.</returns>
  1093.     Private Function GetByte(ByVal address As Integer) As Byte
  1094.  
  1095.         Return Buffer.GetByte(array:=Me.RawData,
  1096.                               index:=Me.HeaderLocation + address)
  1097.  
  1098.     End Function
  1099.  
  1100.     ''' <summary>
  1101.     ''' Gets the specified range of bytes from the raw byte-data.
  1102.     ''' </summary>
  1103.     ''' <param name="from">From address.</param>
  1104.     ''' <param name="to">To address.</param>
  1105.     ''' <returns>The specified bytes from the raw byte-data.</returns>
  1106.     Private Function GetBytes(ByVal from As Integer,
  1107.                               ByVal [to] As Integer) As Byte()
  1108.  
  1109.         Return Me.RawData.Skip(Me.HeaderLocation + from).Take(([to] - from) + 1).ToArray()
  1110.  
  1111.     End Function
  1112.  
  1113.     ''' <summary>
  1114.     ''' Replaces a single byte in the raw byte-data, with the specified data.
  1115.     ''' </summary>
  1116.     ''' <param name="address">the address.</param>
  1117.     ''' <param name="data">The byte-data.</param>
  1118.     Private Sub SetByte(ByVal address As Integer,
  1119.                        ByVal data As Byte)
  1120.  
  1121.         Buffer.SetByte(array:=Me.rawDataB,
  1122.                        index:=Me.HeaderLocation + address,
  1123.                        value:=data)
  1124.  
  1125.     End Sub
  1126.  
  1127.     ''' <summary>
  1128.     ''' Replaces the specified range of bytes in the raw byte-data, with the specified data.
  1129.     ''' </summary>
  1130.     ''' <param name="from">From address.</param>
  1131.     ''' <param name="to">To address.</param>
  1132.     ''' <param name="data">The byte-data.</param>
  1133.     ''' <exception cref="ArgumentException">The byte-length of the specified data differs from the byte-length to be replaced;data</exception>
  1134.     Private Sub SetBytes(ByVal from As Integer,
  1135.                         ByVal [to] As Integer,
  1136.                         ByVal data As Byte())
  1137.  
  1138.         If data.Length <> (([to] - from) + 1) Then
  1139.             Throw New ArgumentException("The byte-length of the specified data differs from the byte-length to be replaced.", "data")
  1140.  
  1141.         Else
  1142.             Buffer.BlockCopy(src:=data, srcOffset:=0,
  1143.                              dst:=Me.rawDataB, dstOffset:=Me.HeaderLocation + from,
  1144.                              count:=([to] - from) + 1)
  1145.  
  1146.         End If
  1147.  
  1148.     End Sub
  1149.  
  1150.     ''' <summary>
  1151.     ''' Sets the ROM name.
  1152.     ''' </summary>
  1153.     ''' <param name="name">The ROM name.</param>
  1154.     ''' <exception cref="ArgumentNullException">name</exception>
  1155.     ''' <exception cref="ArgumentException">The name should contain 21 or less characters;name.</exception>
  1156.     Private Sub SetName(ByVal name As String)
  1157.  
  1158.         Dim fixedNameLength As Integer = (Me.endAddressName - Me.startAddressName) + 1
  1159.  
  1160.         If String.IsNullOrEmpty(name) Then
  1161.             Throw New ArgumentNullException(paramName:="name")
  1162.  
  1163.         ElseIf (name.Length > fixedNameLength) Then
  1164.             Throw New ArgumentException(message:="The name should contain 21 or less characters.", paramName:="name")
  1165.  
  1166.         Else
  1167.             ' fill with spaces up to 21 character length.
  1168.             name = name.PadRight(totalWidth:=fixedNameLength, paddingChar:=" "c)
  1169.  
  1170.             Me.SetBytes(Me.startAddressName, Me.endAddressName, Encoding.ASCII.GetBytes(name))
  1171.  
  1172.         End If
  1173.  
  1174.     End Sub
  1175.  
  1176. #End Region
  1177.  
  1178. #Region " Public Methods "
  1179.  
  1180.     ''' <summary>
  1181.     ''' Save the ROM changes to the specified file path.
  1182.     ''' </summary>
  1183.     ''' <param name="filePath">The ROM file path.</param>
  1184.     ''' <param name="replace">
  1185.     ''' If set to <c>true</c>, then replaces any existing file,
  1186.     ''' otherwise, throws an <see cref="IOException"/> exception if file already exists.
  1187.     ''' </param>
  1188.     ''' <exception cref="IOException">The destination file already exists.</exception>
  1189.     Public Sub Save(ByVal filePath As String, ByVal replace As Boolean)
  1190.  
  1191.         If Not replace AndAlso File.Exists(filePath) Then
  1192.             Throw New IOException(message:="The destination file already exists.")
  1193.  
  1194.         Else
  1195.             Try
  1196.                 File.WriteAllBytes(filePath, Me.rawDataB)
  1197.  
  1198.             Catch ex As Exception
  1199.                 Throw
  1200.  
  1201.             End Try
  1202.  
  1203.         End If
  1204.  
  1205.     End Sub
  1206.  
  1207. #End Region
  1208.  
  1209. End Class
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement