├── .gitattributes
├── .gitignore
├── ACNESCreator.CommandLine
├── ACNESCreator.CommandLine.csproj
├── Program.cs
└── Properties
│ └── PublishProfiles
│ └── FolderProfile.pubxml
├── ACNESCreator.Core
├── ACNESCreator.Core.csproj
├── GCI.cs
├── GCNToolKit.dll
├── NES.cs
├── Patch.cs
├── Utility.cs
└── Yaz0.cs
├── ACNESCreator.FrontEnd
├── ACNESCreator.FrontEnd.csproj
├── App.config
├── App.xaml
├── App.xaml.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Tags
│ └── TagInfo.cs
├── famicom.ico
└── packages.config
├── ACNESCreator.sln
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/ACNESCreator.CommandLine/ACNESCreator.CommandLine.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.2
6 | ACNESCreator.CommandLine.Program
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | win-x64;linux-x64;osx-64
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ACNESCreator.CommandLine/Program.cs:
--------------------------------------------------------------------------------
1 | using ACNESCreator.Core;
2 | using System;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace ACNESCreator.CommandLine
8 | {
9 | class Program
10 | {
11 | readonly static string[] RegionCodes = new string[4] { "J", "E", "P", "U" };
12 |
13 | static void Main(string[] args)
14 | {
15 | // Enable Shift-JIS support.
16 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
17 |
18 | string inputName = null;
19 | string outputName = null;
20 | string romName = null;
21 | string tagsFileName = null;
22 | string region = null;
23 | var ePlus = false;
24 | var canSave = true;
25 | var forceCompress = false;
26 |
27 | if (args == null || args.Length < 1)
28 | {
29 | Console.WriteLine("usage: ACNESCreator.CommandLine.exe nesRomFile outputFile");
30 | }
31 | else
32 | {
33 | for (var i = 0; i < args.Length; i++)
34 | {
35 | if (i + 1 < args.Length)
36 | {
37 | switch (args[i].ToLower())
38 | {
39 | case "-i":
40 | case "-input":
41 | i++;
42 | inputName = args[i];
43 | break;
44 |
45 | case "-o":
46 | case "-output":
47 | i++;
48 | outputName = args[i];
49 | break;
50 |
51 | case "-n":
52 | case "-name":
53 | i++;
54 | romName = args[i];
55 | break;
56 |
57 | case "-nosave":
58 | canSave = false;
59 | break;
60 |
61 | case "-c":
62 | case "-compress":
63 | forceCompress = true;
64 | break;
65 |
66 | case "-e":
67 | case "-eplus":
68 | ePlus = true;
69 | region = "J";
70 | break;
71 |
72 | case "-t":
73 | case "-tags":
74 | i++;
75 | tagsFileName = args[i];
76 | break;
77 |
78 | case "-r":
79 | case "-region":
80 | i++;
81 | if (!ePlus) region = args[i];
82 | break;
83 |
84 | default:
85 | if (inputName == null)
86 | {
87 | inputName = args[i + 1];
88 | }
89 | else if (outputName == null)
90 | {
91 | outputName = args[i + 1];
92 | }
93 | break;
94 | }
95 | }
96 | }
97 |
98 | if (inputName == null || !File.Exists(inputName))
99 | {
100 | Console.WriteLine("The input file doesn't exist!");
101 | }
102 | else
103 | {
104 | if (string.IsNullOrWhiteSpace(outputName))
105 | {
106 | Console.WriteLine("An output file name must be supplied!");
107 | }
108 | else
109 | {
110 | if (tagsFileName != null && !File.Exists(tagsFileName))
111 | {
112 | Console.WriteLine("The tags file doesn't exist! Default tags will be generated.");
113 | tagsFileName = null;
114 | }
115 |
116 | if (romName != null && romName.Trim().Length == 0)
117 | {
118 | Console.WriteLine("The name supplied is invalid. Default name will be used.");
119 | romName = "Custom NES Game";
120 | }
121 |
122 | if (region != null && !RegionCodes.Contains(region.ToUpper()))
123 | {
124 | Console.WriteLine("The region code supplied is invalid. Region code E (NTSC-U, Animal Crossing) will be used.");
125 | region = "E";
126 | }
127 |
128 | // Generate the file with the supplied arguments.
129 | try
130 | {
131 | Stream tagsStream = null;
132 | if (tagsFileName != null)
133 | {
134 | tagsStream = File.OpenRead(tagsFileName);
135 | }
136 |
137 | using (tagsStream)
138 | {
139 | NES nesFile = new NES(romName, File.ReadAllBytes(inputName), Path.GetExtension(inputName), forceCompress,
140 | (Region)Array.IndexOf(RegionCodes, region), canSave, ePlus, null,
141 | tagsStream);
142 |
143 | var outputFile = Path.Combine(Path.GetDirectoryName(inputName),
144 | $"{Path.GetFileNameWithoutExtension(outputName)}.gci");
145 |
146 | using (var stream = new FileStream(outputFile, FileMode.OpenOrCreate))
147 | {
148 | var data = nesFile.GenerateGCIFile();
149 | PrintChecksum(data);
150 | stream.Write(data, 0, data.Length);
151 | }
152 |
153 | Console.WriteLine("Successfully generated a GCI file with the NES rom in it!");
154 | Console.WriteLine($"File Location: {outputFile}");
155 | }
156 | }
157 | catch (Exception e)
158 | {
159 | Console.WriteLine("An error occurred during file generation.");
160 | Console.WriteLine(e.Message);
161 | Console.WriteLine(e.StackTrace);
162 | }
163 | }
164 | }
165 | }
166 |
167 | Console.WriteLine("Press any key to close the window...");
168 | Console.ReadKey();
169 | }
170 |
171 | internal static void PrintChecksum(byte[] Data)
172 | {
173 | byte Checksum = 0;
174 | for (int i = 0x40; i < Data.Length; i++) // Skip the header by adding 0x40
175 | {
176 | Checksum += Data[i];
177 | }
178 |
179 | Console.WriteLine("Checksum: 0x" + Checksum.ToString("X2"));
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/ACNESCreator.CommandLine/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | Release
9 | Any CPU
10 | netcoreapp2.2
11 | bin\Release\netcoreapp2.2\publish\
12 | win-x64
13 |
14 |
--------------------------------------------------------------------------------
/ACNESCreator.Core/ACNESCreator.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.2;net48
5 |
6 |
7 |
8 |
9 | GCNToolKit.dll
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ACNESCreator.Core/GCI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ACNESCreator.Core
7 | {
8 | public enum BannerFormat
9 | {
10 | None = 0,
11 | CI8 = 1,
12 | RGB5A3 = 2
13 | }
14 |
15 | public enum IconFormat
16 | {
17 | None = 0,
18 | CI8 = 1,
19 | RGB5A3 = 2
20 | }
21 |
22 | public enum AnimationSpeed
23 | {
24 | None = 0,
25 | Fast = 1,
26 | Medium = 2,
27 | Slow = 3
28 | }
29 |
30 | public enum Attribute : byte
31 | {
32 | Reserved0 = 0x01,
33 | Reserved1 = 0x02,
34 | Public = 0x04,
35 | NoCopy = 0x08,
36 | NoMove = 0x10,
37 | Global = 0x20,
38 | Company = 0x40,
39 | Reserved7 = 0x80
40 | }
41 |
42 | public sealed class GCI
43 | {
44 | #region DEFAULT ICON IMAGE DATA
45 | public static readonly byte[] DefaultIconData = new byte[]
46 | {
47 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
48 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
49 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x07, 0x28,
50 | 0x02, 0x02, 0x02, 0x00, 0x07, 0x28, 0x89, 0x8F, 0x02, 0x00, 0x07, 0x1F, 0x86, 0x8F, 0x8F, 0x8F,
51 | 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x28, 0x0C, 0x05, 0x00, 0x02, 0x02, 0x02, 0x02,
52 | 0x8F, 0x8C, 0x79, 0x1F, 0x0C, 0x00, 0x00, 0x02, 0x8F, 0x8F, 0x8F, 0x8F, 0x8C, 0x75, 0x1F, 0x0C,
53 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
54 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
55 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x03, 0x10,
56 | 0x02, 0x02, 0x02, 0x02, 0x00, 0x10, 0x79, 0x8F, 0x02, 0x02, 0x00, 0x0C, 0x75, 0x8F, 0x8F, 0x8F,
57 | 0x05, 0x1E, 0x81, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x7C, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
58 | 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
59 | 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8C, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8C, 0x6B,
60 | 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x7C, 0x65, 0x7B, 0x8F, 0x8F, 0x8F, 0x89, 0x6B, 0x6E, 0x81, 0x85,
61 | 0x4C, 0x14, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x38, 0x33, 0x35, 0x31, 0x14, 0x07, 0x02, 0x02,
62 | 0x79, 0x60, 0x35, 0x38, 0x7C, 0x8B, 0x53, 0x0A, 0x81, 0x6E, 0x6B, 0x8C, 0x8F, 0x75, 0x53, 0x1B,
63 | 0x02, 0x0C, 0x29, 0x8C, 0x8B, 0x8C, 0x8F, 0x8F, 0x0E, 0x6E, 0x81, 0x79, 0x81, 0x7C, 0x79, 0x81,
64 | 0x12, 0x6E, 0x3E, 0x6B, 0x82, 0x8E, 0x8D, 0x86, 0x12, 0x79, 0x59, 0x84, 0x7D, 0x71, 0x73, 0x89,
65 | 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8C, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
66 | 0x79, 0x79, 0x86, 0x8D, 0x8F, 0x8F, 0x8D, 0x75, 0x8F, 0x8D, 0x7E, 0x79, 0x7E, 0x7C, 0x6B, 0x7C,
67 | 0x8F, 0x8D, 0x79, 0x69, 0x7C, 0x81, 0x81, 0x79, 0x86, 0x6B, 0x75, 0x81, 0x81, 0x7E, 0x6B, 0x73,
68 | 0x6B, 0x7E, 0x81, 0x81, 0x79, 0x6B, 0x81, 0x8F, 0x85, 0x81, 0x7E, 0x6E, 0x75, 0x8D, 0x8F, 0x6E,
69 | 0x65, 0x7E, 0x8F, 0x89, 0x53, 0x49, 0x58, 0x1B, 0x8D, 0x8F, 0x73, 0x49, 0x4C, 0x58, 0x58, 0x1D,
70 | 0x89, 0x53, 0x49, 0x53, 0x58, 0x58, 0x4C, 0x18, 0x45, 0x4C, 0x58, 0x58, 0x53, 0x45, 0x3A, 0x18,
71 | 0x10, 0x75, 0x60, 0x49, 0x70, 0x83, 0x77, 0x67, 0x11, 0x51, 0x54, 0x60, 0x5A, 0x4E, 0x53, 0x65,
72 | 0x0D, 0x6A, 0x5E, 0x52, 0x55, 0x5C, 0x65, 0x5F, 0x00, 0x26, 0x47, 0x32, 0x47, 0x56, 0x51, 0x54,
73 | 0x6B, 0x7C, 0x8C, 0x8C, 0x53, 0x30, 0x45, 0x65, 0x6E, 0x6E, 0x6B, 0x44, 0x30, 0x36, 0x35, 0x31,
74 | 0x53, 0x50, 0x65, 0x3E, 0x2B, 0x2E, 0x30, 0x3E, 0x65, 0x65, 0x60, 0x38, 0x2B, 0x2C, 0x2B, 0x58,
75 | 0x7C, 0x79, 0x6B, 0x85, 0x8F, 0x86, 0x4C, 0x45, 0x35, 0x75, 0x8F, 0x8F, 0x6E, 0x45, 0x4C, 0x58,
76 | 0x81, 0x8F, 0x86, 0x4C, 0x45, 0x58, 0x58, 0x57, 0x6B, 0x63, 0x45, 0x4C, 0x58, 0x58, 0x4C, 0x3E,
77 | 0x53, 0x58, 0x58, 0x49, 0x39, 0x42, 0x47, 0x07, 0x58, 0x53, 0x3E, 0x3C, 0x48, 0x52, 0x37, 0x02,
78 | 0x49, 0x3A, 0x43, 0x4B, 0x4F, 0x42, 0x0E, 0x02, 0x3D, 0x4A, 0x4B, 0x4A, 0x37, 0x07, 0x02, 0x02,
79 | 0x02, 0x07, 0x4A, 0x32, 0x2E, 0x56, 0x6D, 0x64, 0x02, 0x00, 0x1A, 0x5D, 0x5D, 0x6A, 0x6C, 0x6A,
80 | 0x02, 0x02, 0x02, 0x07, 0x19, 0x4A, 0x64, 0x6D, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x0B, 0x24,
81 | 0x56, 0x51, 0x61, 0x38, 0x2B, 0x2E, 0x2E, 0x75, 0x6C, 0x6C, 0x64, 0x34, 0x2B, 0x2B, 0x2E, 0x74,
82 | 0x6C, 0x6A, 0x6C, 0x3F, 0x2B, 0x2C, 0x2E, 0x4D, 0x5D, 0x6C, 0x6D, 0x3F, 0x2B, 0x2B, 0x2F, 0x5E,
83 | 0x79, 0x49, 0x4C, 0x58, 0x53, 0x45, 0x3A, 0x47, 0x7E, 0x58, 0x4C, 0x4C, 0x3B, 0x41, 0x4B, 0x4B,
84 | 0x6B, 0x4C, 0x39, 0x3C, 0x48, 0x4B, 0x4B, 0x47, 0x5D, 0x34, 0x37, 0x41, 0x47, 0x4B, 0x37, 0x0A,
85 | 0x4B, 0x4B, 0x42, 0x16, 0x05, 0x02, 0x02, 0x02, 0x4A, 0x37, 0x0A, 0x02, 0x02, 0x02, 0x02, 0x02,
86 | 0x17, 0x05, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
87 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
88 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
89 | 0x07, 0x1C, 0x52, 0x3A, 0x2B, 0x2B, 0x2F, 0x5E, 0x02, 0x02, 0x05, 0x09, 0x20, 0x2E, 0x2D, 0x40,
90 | 0x02, 0x02, 0x00, 0x08, 0x05, 0x06, 0x13, 0x4D, 0x02, 0x0C, 0x28, 0x73, 0x65, 0x12, 0x06, 0x27,
91 | 0x5D, 0x66, 0x81, 0x80, 0x54, 0x21, 0x04, 0x02, 0x88, 0x8C, 0x3E, 0x2F, 0x60, 0x6B, 0x0C, 0x00,
92 | 0x79, 0x86, 0x46, 0x2A, 0x33, 0x72, 0x7A, 0x1E, 0x8A, 0x6B, 0x85, 0x7C, 0x5B, 0x83, 0x83, 0x7F,
93 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
94 | 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x28, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
95 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x05, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07,
96 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
97 | 0x23, 0x89, 0x7E, 0x2B, 0x2F, 0x79, 0x6F, 0x4E, 0x58, 0x60, 0x75, 0x7C, 0x65, 0x59, 0x71, 0x84,
98 | 0x63, 0x75, 0x65, 0x58, 0x6E, 0x7C, 0x78, 0x7D, 0x07, 0x1B, 0x60, 0x75, 0x6B, 0x53, 0x62, 0x76,
99 | 0x75, 0x8A, 0x6E, 0x7C, 0x86, 0x68, 0x83, 0x7D, 0x72, 0x73, 0x8A, 0x79, 0x6E, 0x86, 0x7C, 0x87,
100 | 0x70, 0x76, 0x6B, 0x86, 0x7E, 0x6B, 0x7E, 0x65, 0x7C, 0x7C, 0x45, 0x49, 0x81, 0x81, 0x49, 0x65,
101 | 0x87, 0x7E, 0x0A, 0x02, 0x02, 0x02, 0x02, 0x02, 0x81, 0x6B, 0x38, 0x02, 0x02, 0x02, 0x02, 0x02,
102 | 0x53, 0x65, 0x49, 0x02, 0x02, 0x02, 0x02, 0x02, 0x58, 0x1B, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02,
103 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
104 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
105 | 0x02, 0x02, 0x04, 0x0F, 0x53, 0x75, 0x6E, 0x53, 0x02, 0x02, 0x02, 0x02, 0x01, 0x0A, 0x25, 0x75,
106 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
107 | 0x4C, 0x4C, 0x4C, 0x22, 0x0C, 0x6E, 0x45, 0x1B, 0x50, 0x3E, 0x0F, 0x02, 0x02, 0x05, 0x07, 0x02,
108 | 0x15, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
109 | 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
110 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
111 | 0x02, 0x22, 0x03, 0x32, 0x03, 0x33, 0x12, 0x22, 0x13, 0x32, 0x13, 0x33, 0x22, 0x22, 0x23, 0x33,
112 | 0x24, 0x44, 0x32, 0x23, 0x33, 0x33, 0x34, 0x34, 0x34, 0x44, 0x34, 0x45, 0x43, 0x33, 0x44, 0x44,
113 | 0x45, 0x55, 0x45, 0x56, 0x46, 0x66, 0x51, 0x11, 0x52, 0x22, 0x53, 0x33, 0x53, 0x34, 0x54, 0x34,
114 | 0x54, 0x44, 0x55, 0x45, 0x55, 0x46, 0x55, 0x55, 0x55, 0x56, 0x56, 0x65, 0x56, 0x66, 0x57, 0x77,
115 | 0x62, 0x22, 0x63, 0x34, 0x64, 0x44, 0x65, 0x55, 0x66, 0x57, 0x66, 0x66, 0x66, 0x68, 0x67, 0x77,
116 | 0x68, 0x88, 0x6A, 0xAA, 0x84, 0x21, 0x8C, 0x63, 0x90, 0x63, 0x90, 0x64, 0x90, 0x84, 0x94, 0xA5,
117 | 0x98, 0xC6, 0x9C, 0xE7, 0x9C, 0xE8, 0xA1, 0x08, 0xA1, 0x0A, 0xA5, 0x29, 0xA9, 0x29, 0xA9, 0x2C,
118 | 0xA9, 0x4A, 0xA9, 0x4B, 0xA9, 0x4C, 0xAD, 0x4B, 0xAD, 0x4D, 0xAD, 0x4E, 0xAD, 0x6B, 0xAD, 0x6D,
119 | 0xB1, 0x6D, 0xB1, 0x6E, 0xB1, 0x6F, 0xB1, 0x70, 0xB1, 0x8B, 0xB1, 0x8C, 0xB5, 0x8C, 0xB5, 0x90,
120 | 0xB5, 0x91, 0xB5, 0xAD, 0xB9, 0xB1, 0xB9, 0xB2, 0xB9, 0xCE, 0xB9, 0xCF, 0xBA, 0x0F, 0xBD, 0xB2,
121 | 0xBD, 0xCE, 0xBD, 0xD1, 0xBD, 0xD3, 0xBD, 0xEF, 0xC1, 0xF1, 0xC1, 0xF2, 0xC1, 0xF4, 0xC2, 0x0F,
122 | 0xC2, 0x10, 0xC2, 0x31, 0xC2, 0x51, 0xC6, 0x10, 0xC6, 0x11, 0xC6, 0x15, 0xC6, 0x16, 0xC6, 0x30,
123 | 0xC6, 0x31, 0xC6, 0x32, 0xC6, 0x52, 0xCA, 0x31, 0xCA, 0x37, 0xCA, 0x52, 0xCA, 0x53, 0xCA, 0x73,
124 | 0xCD, 0xEF, 0xCE, 0x52, 0xCE, 0x58, 0xCE, 0x73, 0xD2, 0x58, 0xD2, 0x79, 0xD2, 0x94, 0xD2, 0xB5,
125 | 0xD5, 0x4A, 0xD5, 0xAD, 0xD6, 0x73, 0xD6, 0x94, 0xD6, 0xB4, 0xD6, 0xB5, 0xD6, 0xD6, 0xDA, 0x10,
126 | 0xDA, 0x94, 0xDA, 0xD6, 0xDA, 0xF7, 0xDE, 0xD6, 0xDE, 0xF7, 0xE1, 0x29, 0xE3, 0x18, 0xE6, 0xB5,
127 | 0xE7, 0x38, 0xE7, 0x39, 0xE7, 0x5A, 0xE8, 0xA5, 0xE9, 0xCE, 0xEB, 0x39, 0xEB, 0x5A, 0xEB, 0x7B,
128 | 0xEF, 0x7A, 0xEF, 0x7B, 0xF3, 0x7B, 0xF3, 0x9C, 0xF7, 0xBD, 0xFB, 0xDE, 0xFB, 0xFF, 0xFF, 0xFF,
129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 | };
144 | #endregion
145 |
146 | internal static char[] PadString(string Input, int Length)
147 | {
148 | char[] Out = Input.ToCharArray();
149 | Array.Resize(ref Out, Length);
150 | return Out;
151 | }
152 |
153 | const int GCIHeaderSize = 0x40;
154 | const int BlockSize = 0x2000;
155 | internal static readonly long Epoch = new DateTime(2000, 1, 1).Ticks; // Ticks after UTC epoch on Jan 1st, 2000.
156 |
157 | public GCIHeader Header;
158 | public string Comment1;
159 | public string Comment2;
160 | public byte[] ImageData;
161 | public byte[] Data
162 | {
163 | get => _data;
164 | set
165 | {
166 | _data = value;
167 | Header.BlockCount = (ushort)((value.Length / BlockSize) + 1);
168 | }
169 | }
170 |
171 | private byte[] _data;
172 |
173 | public sealed class GCIHeader
174 | {
175 | public string GameCode;
176 | public string MakerCode;
177 | public byte Unused1;
178 | public byte BIFlags;
179 | public string FileName;
180 | public int ModTime;
181 | public int ImageOffset;
182 | public ushort IconFormat;
183 | public ushort AnimationSpeed;
184 | public byte PermissionsFlags;
185 | public byte CopyCounter;
186 | public ushort FirstBlock;
187 | public ushort BlockCount;
188 | public ushort Unused2;
189 | public int CommentOffset;
190 |
191 | public GCIHeader(byte[] Data)
192 | {
193 | GameCode = Encoding.ASCII.GetString(Data, 0, 4);
194 | MakerCode = Encoding.ASCII.GetString(Data, 3, 2);
195 | Unused1 = Data[6];
196 | BIFlags = Data[7];
197 | FileName = Encoding.ASCII.GetString(Data, 8, 32);
198 | ModTime = BitConverter.ToInt32(Data, 0x28).Reverse();
199 | ImageOffset = BitConverter.ToInt32(Data, 0x2C).Reverse();
200 | IconFormat = BitConverter.ToUInt16(Data, 0x30).Reverse();
201 | AnimationSpeed = BitConverter.ToUInt16(Data, 0x32).Reverse();
202 | PermissionsFlags = Data[0x34];
203 | CopyCounter = Data[0x35];
204 | FirstBlock = BitConverter.ToUInt16(Data, 0x36).Reverse();
205 | BlockCount = BitConverter.ToUInt16(Data, 0x38).Reverse();
206 | Unused2 = BitConverter.ToUInt16(Data, 0x3A).Reverse();
207 | CommentOffset = BitConverter.ToInt32(Data, 0x3C).Reverse();
208 | }
209 |
210 | public GCIHeader()
211 | {
212 | GameCode = "GAFE";
213 | MakerCode = "01";
214 | Unused1 = 0xFF;
215 | BIFlags = 0; // No banner
216 | FileName = "DobutsunomoriP_F_GAME";
217 | ModTime = (int)new TimeSpan(DateTime.Now.Ticks - Epoch).TotalSeconds;
218 | ImageOffset = 0x40;
219 | IconFormat = (ushort)Core.IconFormat.CI8;
220 | AnimationSpeed = (ushort)Core.AnimationSpeed.Medium;
221 | PermissionsFlags = (byte)Attribute.NoCopy;
222 | CopyCounter = 0;
223 | FirstBlock = 0;
224 | BlockCount = 1;
225 | Unused2 = 0xFFFF;
226 | CommentOffset = 0;
227 | }
228 |
229 | public byte[] GetData()
230 | {
231 | byte[] HeaderData = new byte[GCIHeaderSize];
232 | Encoding.ASCII.GetBytes(PadString(GameCode, 4), 0, 4).CopyTo(HeaderData, 0);
233 | Encoding.ASCII.GetBytes(PadString(MakerCode, 2), 0, 2).CopyTo(HeaderData, 4);
234 | HeaderData[6] = Unused1;
235 | HeaderData[7] = BIFlags;
236 | Encoding.ASCII.GetBytes(PadString(FileName, 32), 0, 32).CopyTo(HeaderData, 8);
237 | BitConverter.GetBytes(ModTime.Reverse()).CopyTo(HeaderData, 0x28);
238 | BitConverter.GetBytes(ImageOffset.Reverse()).CopyTo(HeaderData, 0x2C);
239 | BitConverter.GetBytes(IconFormat.Reverse()).CopyTo(HeaderData, 0x30);
240 | BitConverter.GetBytes(AnimationSpeed.Reverse()).CopyTo(HeaderData, 0x32);
241 | HeaderData[0x34] = PermissionsFlags;
242 | HeaderData[0x35] = CopyCounter;
243 | BitConverter.GetBytes(FirstBlock.Reverse()).CopyTo(HeaderData, 0x36);
244 | BitConverter.GetBytes(BlockCount.Reverse()).CopyTo(HeaderData, 0x38);
245 | BitConverter.GetBytes(Unused2.Reverse()).CopyTo(HeaderData, 0x3A);
246 | BitConverter.GetBytes(CommentOffset.Reverse()).CopyTo(HeaderData, 0x3C);
247 |
248 | return HeaderData;
249 | }
250 | }
251 |
252 | public GCI(byte[] IconData = null)
253 | {
254 | Header = new GCIHeader();
255 | Comment1 = "Animal Crossing";
256 | Comment2 = "NES Game";
257 | ImageData = IconData ?? DefaultIconData;
258 | Data = new byte[0];
259 | }
260 |
261 | public GCI(byte[] HeaderData, byte[] CommentData, byte[] BannerData, byte[] IconData)
262 | {
263 | Header = new GCIHeader(HeaderData);
264 | Comment1 = Encoding.GetEncoding("Shift-JIS").GetString(CommentData, 0, 32);
265 | Comment2 = Encoding.GetEncoding("Shift-JIS").GetString(CommentData, 0x20, 32);
266 | if ((IconFormat)Header.IconFormat == IconFormat.CI8)
267 | {
268 | ImageData = IconData.Skip(Header.ImageOffset + GCIHeaderSize).Take(0x600).ToArray();
269 | }
270 |
271 | Data = Data.Skip(0x640).ToArray();
272 | }
273 |
274 | public byte[] GetData()
275 | {
276 | List Data = new List();
277 | Data.AddRange(Header.GetData());
278 | while (Header.CommentOffset >= Data.Count)
279 | {
280 | Data.Add(0);
281 | }
282 |
283 | byte[] Comment1Data = Encoding.GetEncoding("shift-jis").GetBytes(Comment1);
284 | Array.Resize(ref Comment1Data, 32);
285 | Data.AddRange(Comment1Data);
286 | byte[] Comment2Data = Encoding.GetEncoding("shift-jis").GetBytes(Comment2);
287 | Array.Resize(ref Comment2Data, 32);
288 | Data.AddRange(Comment2Data);
289 |
290 | while (Header.ImageOffset >= Data.Count)
291 | {
292 | Data.Add(0);
293 | }
294 |
295 | Data.AddRange(ImageData);
296 |
297 | while (Data.Count < 0x680)
298 | {
299 | Data.Add(0); // Pad to 0x680
300 | }
301 |
302 | Data.AddRange(this.Data);
303 |
304 | byte[] Output = Data.ToArray();
305 | Array.Resize(ref Output, GCIHeaderSize + Header.BlockCount * 0x2000);
306 | return Output;
307 | }
308 | }
309 | }
310 |
--------------------------------------------------------------------------------
/ACNESCreator.Core/GCNToolKit.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuyler36/ACNESCreator/167dd6f69e728ae64a01a8bc29f7f7c4d21bf586/ACNESCreator.Core/GCNToolKit.dll
--------------------------------------------------------------------------------
/ACNESCreator.Core/NES.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace ACNESCreator.Core
8 | {
9 | public enum Region
10 | {
11 | Japan = 0,
12 | NorthAmerica = 1,
13 | Europe = 2,
14 | Australia = 3
15 | }
16 |
17 | public class NES
18 | {
19 | public static NES Instance;
20 |
21 | public const int MaxROMSize = 0xFFFF0;
22 | const uint PatchAdjustOffset = 0x7F800000;
23 |
24 | /*
25 | * Flags:
26 | * Flags for flag1:
27 | * bool hasEmbeddedSaveFileImage = (flags >> 7) & 1 == 1;
28 | *
29 | *
30 | * Flags for flag2:
31 | * BannerFormat bannerFormat = (BannerFormat) ((flags >> 5) & 3);
32 | */
33 |
34 | const byte DefaultFlags1 = 0xEA;
35 | const byte DefaultFlags2 = 0;
36 | static readonly byte[] DefaultTagData = Encoding.ASCII.GetBytes("TAG\0" + "GNO\0x1F" + "END\0");
37 | static readonly string[] RegionCodes = new string[4] { "J", "E", "P", "U" };
38 | static readonly string[] TagList = new string[]
39 | {
40 | "END", "VEQ", "VNE", "GID", "GNM", "CPN", "OFS", "HSC",
41 | "GNO", "BBR", "QDS", "SPE", "ISZ", "IFM", "REM", "TCS",
42 | "ICS", "ESZ", "FIL", "ROM", "MOV", "NHD", "DIF", "PAT"
43 | };
44 |
45 | public class ACNESHeader
46 | {
47 | public byte Checksum = 0; // May not be the checksum byte. Initialized to 0x47 when not loading a memcard game.
48 | public byte Unknown = 0; // May not be used. Seems to be initialized to zero when loading a memcard game. Could be a bool meant to show if game is memcard or not.
49 | public string Name;
50 | public ushort DataSize;
51 | public ushort TagsSize;
52 | public ushort IconFormat;
53 | public ushort IconFlags = 0; // IconFlags. Unsure what they do, but they're set in "SetupResIcon". Maybe they're not important? It's also passed as an argument to memcard_data_save, but appears to go unused.
54 | public ushort BannerSize;
55 | public byte Flags1;
56 | public byte Flags2;
57 | public ushort Padding; // 0 Padding? Doesn't appear to be used.
58 |
59 | public byte[] GetData(Region GameRegion)
60 | {
61 | byte[] Data = new byte[0x20];
62 |
63 | Data[0] = Checksum;
64 | Data[1] = Unknown;
65 | if (GameRegion == Region.Japan) // Japan games have item strings that are 10 characters long, compared to the 16 characters in non-Japanese games.
66 | {
67 | Utility.GetPaddedStringData(Name, 0xA, 0x20).CopyTo(Data, 2);
68 | BitConverter.GetBytes(DataSize.Reverse()).CopyTo(Data, 0xC);
69 | }
70 | else
71 | {
72 | Utility.GetPaddedStringData(Name, 0x10, 0x20).CopyTo(Data, 2);
73 | BitConverter.GetBytes(DataSize.Reverse()).CopyTo(Data, 0x12);
74 | }
75 | BitConverter.GetBytes(TagsSize.Reverse()).CopyTo(Data, 0x14);
76 | BitConverter.GetBytes(IconFormat.Reverse()).CopyTo(Data, 0x16);
77 | BitConverter.GetBytes(IconFlags.Reverse()).CopyTo(Data, 0x18);
78 | BitConverter.GetBytes(BannerSize.Reverse()).CopyTo(Data, 0x1A);
79 | Data[0x1C] = Flags1;
80 | Data[0x1D] = Flags2;
81 | BitConverter.GetBytes(Padding.Reverse()).CopyTo(Data, 0x1E);
82 |
83 | return Data;
84 | }
85 | }
86 |
87 | public class Banner
88 | {
89 | public string Title;
90 | public string Comment;
91 | public byte[] BannerData = new byte[0];
92 | public byte[] IconData = new byte[0];
93 |
94 | public byte[] GetData()
95 | {
96 | List Data = new List();
97 | if (!string.IsNullOrEmpty(Title) || !string.IsNullOrEmpty(Comment))
98 | {
99 | Data.AddRange(Utility.GetPaddedStringData(Title, 0x20));
100 | Data.AddRange(Utility.GetPaddedStringData(Comment, 0x20));
101 | }
102 |
103 | Data.AddRange(BannerData);
104 | Data.AddRange(IconData);
105 |
106 | return Data.ToArray();
107 | }
108 |
109 | public ushort GetSize()
110 | => (ushort)(((!string.IsNullOrEmpty(Title) || !string.IsNullOrEmpty(Comment)) ? 0x40 : 0) + BannerData.Length + IconData.Length);
111 | }
112 |
113 | public readonly ACNESHeader Header;
114 | public byte[] TagData { get; internal set; }
115 | public readonly Banner BannerData;
116 | public readonly byte[] ROM;
117 |
118 | public readonly Region GameRegion;
119 | public readonly bool IsDnMe;
120 | public readonly byte[] SaveIconData;
121 | public readonly bool IsROM;
122 | public readonly bool IsQD;
123 | public readonly bool IsFDS;
124 |
125 | private readonly int originalROMSize;
126 |
127 | public NES(string ROMName, byte[] ROMData, string ext, Region ACRegion, bool Compress, bool IsGameDnMe,
128 | byte[] IconData = null, Stream tagsStream = null)
129 | {
130 | // Save the original size of the ROM image
131 | originalROMSize = ROMData.Length;
132 |
133 | // Set the icon
134 | SaveIconData = IconData ?? GCI.DefaultIconData;
135 |
136 | // Is game Doubutsu no Mori e+?
137 | IsDnMe = IsGameDnMe;
138 |
139 | // If Data is Yaz0 compressed, uncompress it first
140 | if (Yaz0.IsYaz0(ROMData))
141 | {
142 | ROMData = Yaz0.Decompress(ROMData);
143 | Compress = true;
144 | }
145 |
146 | IsROM = IsNESImage(ROMData);
147 | if (!IsROM)
148 | {
149 | IsQD = IsQDImage(ROMData, ext);
150 | if (!IsQD)
151 | {
152 | IsFDS = IsFDSImage(ROMData);
153 | }
154 | }
155 |
156 | // Convert .fds to .qd
157 | if (IsFDS)
158 | {
159 | ROMData = Utility.ConvertFDSToQD(ROMData);
160 | }
161 |
162 | if (ROMName == null || ROMName.Length < 4 || ROMName.Length > 0x10)
163 | {
164 | throw new ArgumentException("ROMName cannot be less than 4 characters or longer than 16 characters.");
165 | }
166 |
167 | // Compress the ROM if compression is requested
168 | if (Compress)
169 | {
170 | ROMData = Yaz0.Compress(ROMData);
171 | }
172 |
173 | if (!Compress && ROMData.Length > MaxROMSize)
174 | {
175 | throw new ArgumentException(string.Format("This ROM cannot be used, as it is larger than the max ROM size.\r\nThe max ROM size is 0x{0} ({1}) bytes long!",
176 | MaxROMSize.ToString("X"), MaxROMSize.ToString("N0")));
177 | }
178 |
179 | TagData = Utility.GetPaddedData(DefaultTagData, (DefaultTagData.Length + 0xF) & ~0xF);
180 |
181 | BannerData = new Banner
182 | {
183 | Title = IsDnMe ? "Animal Forest e+" : "Animal Crossing",
184 | Comment = ROMName + "\n" + "NES Save Data"
185 | };
186 |
187 | Header = new ACNESHeader
188 | {
189 | Name = ROMName,
190 | DataSize = (ushort)((ROMData.Length + 0xF) >> 4), // If the ROM is compressed, the size is retrieved from the Yaz0 header.
191 | TagsSize = (ushort)((TagData.Length + 0xF) & ~0xF),
192 | IconFormat = (ushort)IconFormat.CI8,
193 | IconFlags = 0,
194 | BannerSize = (ushort)((BannerData.GetSize() + 0xF) & ~0xF),
195 | Flags1 = DefaultFlags1,
196 | Flags2 = DefaultFlags2,
197 | Padding = 0
198 | };
199 |
200 | ROM = ROMData;
201 | GameRegion = ACRegion;
202 |
203 | // Generate custom tag data if possible.
204 | if (tagsStream == null)
205 | {
206 | GenerateDefaultTagData(ROMName, IsROM || IsFDS, ACRegion, IsGameDnMe);
207 | }
208 | else
209 | {
210 | ImportTags(tagsStream);
211 | }
212 | }
213 |
214 | public NES(string ROMName, byte[] ROMData, string ext, bool CanSave, Region ACRegion, bool Compress, bool IsDnMe,
215 | byte[] IconData = null, Stream tagsStream = null)
216 | : this(ROMName, ROMData, ext, ACRegion, Compress, IsDnMe, IconData, tagsStream)
217 | {
218 | if (!CanSave)
219 | {
220 | Header.Flags1 &= 1; // Only save the lowest bit, as everything else is related to saving.
221 | Header.IconFormat = 0;
222 | Header.BannerSize = 0;
223 | }
224 | }
225 |
226 | internal bool IsNESImage(in byte[] data)
227 | => Encoding.ASCII.GetString(data, 0, 3) == "NES";
228 |
229 | internal bool IsFDSImage(in byte[] data)
230 | => Encoding.ASCII.GetString(data, 0, 3) == "FDS" || Encoding.ASCII.GetString(data, 1, 14) == "*NINTENDO-HVC*";
231 |
232 | internal bool IsQDImage(in byte[] data, string ext)
233 | => data[0] == 0x01 && Encoding.ASCII.GetString(data, 1, 14) == "*NINTENDO-HVC*" && ext == ".qd";
234 |
235 | internal void SetChecksum(ref byte[] Data)
236 | {
237 | byte Checksum = 0;
238 | for (int i = 0x40; i < Data.Length; i++) // Skip the header by adding 0x40
239 | {
240 | Checksum += Data[i];
241 | }
242 |
243 | Data[0x680] = (byte)-Checksum;
244 | }
245 |
246 | internal void GenerateDefaultTagData(string GameName, bool NESImage, Region GameRegion, bool IsDnMe)
247 | {
248 | if (GameName.Length > 1)
249 | {
250 | GameName = GameName.Trim().ToUpper();
251 | List> Tags = new List>
252 | {
253 | new KeyValuePair("GID", Encoding.ASCII.GetBytes(GameName.Substring(0, 1) + GameName.Substring(GameName.Length - 1, 1))),
254 | new KeyValuePair("GNM", Encoding.ASCII.GetBytes(GameName))
255 | };
256 |
257 | // Check to see if saving is enabled.
258 | if ((Header.Flags1 & 0x80) != 0)
259 | {
260 | Tags.Add(new KeyValuePair("OFS", new byte[2] { 0x00, 0x00 }));
261 |
262 | if (IsROM)
263 | {
264 | // This works by backing up the entire battery backup RAM.
265 | Tags.Add(new KeyValuePair("BBR", new byte[4] { 0x00, 0x00, 0x20, 0x00 }));
266 | }
267 | else if (IsFDS)
268 | {
269 | // The valid bytes responsible for booting properly via the Famicom Disk System BIOS (noise.bin.szs)
270 | Tags.Add(new KeyValuePair("QDS", new byte[5] { 0x00, 0xa1, 0x70, 0x00, 0x50 }));
271 | }
272 | }
273 |
274 | // Patch ROM if not NES Image
275 | if (!NESImage && GameRegion == Region.NorthAmerica) // TODO: Support DnM+ 1.0, DnM+ 1.1, AC Austrialian, AC European, DnMe+ 1.0, & DnMe+ 1.1
276 | {
277 | byte[] LoaderData = new byte[Patch.PatcherData.Length * 4];
278 | for (int i = 0; i < Patch.PatcherData.Length; i++)
279 | {
280 | BitConverter.GetBytes(Patch.PatcherData[i].Reverse()).CopyTo(LoaderData, i * 4);
281 | }
282 |
283 | AddPatchData(ref Tags, Patch.PatcherEntryPointData, LoaderData);
284 | switch (GameRegion)
285 | {
286 | case Region.Japan:
287 | break;
288 | case Region.Europe:
289 | break;
290 | case Region.Australia:
291 | break;
292 | case Region.NorthAmerica:
293 | default:
294 | AddPatchData(ref Tags, 0x806D4B9C, BitConverter.GetBytes(Patch.PatcherEntryPointData.Reverse()));
295 | break;
296 | }
297 | }
298 |
299 | Tags.Add(new KeyValuePair("END", new byte[0]));
300 |
301 | GenerateTagData(Tags);
302 | }
303 | }
304 |
305 | public void ImportTags(Stream tagsStream)
306 | {
307 | var tags = new List>();
308 |
309 | using (var reader = new BinaryReader(tagsStream))
310 | {
311 | while (tagsStream.Position < tagsStream.Length)
312 | {
313 | var tag = Encoding.ASCII.GetString(reader.ReadBytes(3)).ToUpper();
314 |
315 | // Verify tag is a real tag.
316 | if (!TagList.Contains(tag))
317 | {
318 | Console.WriteLine($"Ignoring {tag} because it's not a valid tag!");
319 | tagsStream.Position++;
320 | continue;
321 | }
322 |
323 | if (tag == "END")
324 | {
325 | // If we hit an END tag, there's no point in continuing to parse the tags.
326 | // The nestag parser will exit there so any tags following will be ignored anyways.
327 | tags.Add(new KeyValuePair("END", new byte[] { 0x00 }));
328 | break;
329 | }
330 |
331 | var size = reader.ReadByte();
332 | tags.Add(new KeyValuePair(tag, size == 0 ? new byte[0] : reader.ReadBytes(size)));
333 | }
334 | }
335 |
336 | // Generate the binary tag data.
337 | GenerateTagData(tags);
338 | }
339 |
340 | internal void AddPatchData(ref List> Tags, uint WriteStartOffset, byte[] PatchData)
341 | {
342 | uint WriteAddress = WriteStartOffset;
343 | uint DataOffset = 0;
344 |
345 | while (WriteAddress < 0x807FFFFF && DataOffset < PatchData.Length)
346 | {
347 | List PatchDataList = new List();
348 | uint WriteAmount = (uint)(PatchData.Length - DataOffset);
349 | if (WriteAmount > 0xFB)
350 | {
351 | WriteAmount = 0xFB;
352 | }
353 |
354 | PatchDataList.Add((byte)((WriteAddress - PatchAdjustOffset) >> 16));
355 | PatchDataList.Add((byte)WriteAmount);
356 | PatchDataList.Add((byte)((WriteAddress & 0xFF00) >> 8));
357 | PatchDataList.Add((byte)(WriteAddress & 0xFF));
358 |
359 | for (uint i = DataOffset; i < DataOffset + WriteAmount; i++)
360 | {
361 | PatchDataList.Add(PatchData[i]);
362 | }
363 |
364 | DataOffset += WriteAmount;
365 | WriteAddress += WriteAmount;
366 |
367 | Tags.Add(new KeyValuePair("PAT", PatchDataList.ToArray()));
368 | }
369 | }
370 |
371 | public void GenerateTagData(List> Tags)
372 | {
373 | bool HasEND = false;
374 | List TagRawData = new List();
375 |
376 | // The first tag is printed and then ignored, so we can put whatever we want here
377 | TagRawData.AddRange(Encoding.ASCII.GetBytes("TAG\0"));
378 |
379 | foreach (var TagInfo in Tags)
380 | {
381 | // Ensure the tag is capitalized
382 | string Tag = TagInfo.Key.ToUpper();
383 |
384 | // Confirm the tag is valid
385 | if (Array.IndexOf(TagList, Tag) > -1)
386 | {
387 | // Add the tag
388 | TagRawData.AddRange(Encoding.ASCII.GetBytes(Tag));
389 |
390 | // Add the tag data length
391 | TagRawData.Add((byte)(TagInfo.Value.Length & 0xFF));
392 |
393 | if (Tag == "END")
394 | {
395 | HasEND = true;
396 | }
397 |
398 | // Add the tag data
399 | TagRawData.AddRange(TagInfo.Value);
400 | }
401 | }
402 |
403 | // Ensure that an END tag is present so the parser will stop
404 | if (!HasEND)
405 | {
406 | TagRawData.AddRange(Encoding.ASCII.GetBytes("END\0"));
407 | }
408 |
409 | byte[] TagBinaryData = TagRawData.ToArray();
410 | TagBinaryData = Utility.GetPaddedData(TagBinaryData, (TagBinaryData.Length + 0xF) & ~0xF);
411 |
412 | Header.TagsSize = (ushort)TagBinaryData.Length;
413 | TagData = TagBinaryData;
414 | }
415 |
416 | public byte[] GenerateGCIFile()
417 | {
418 | List Data = new List();
419 | Data.AddRange(Header.GetData(GameRegion));
420 | Data.AddRange(TagData);
421 | if ((Header.Flags1 & 0x80) == 0x80) // Only add banner if saving is enabled
422 | {
423 | Data.AddRange(BannerData.GetData());
424 | }
425 | Data.AddRange(ROM);
426 |
427 | var BlankGCIFile = new GCI(SaveIconData)
428 | {
429 | Data = Data.ToArray(),
430 | Comment1 = IsDnMe ? "Animal Forest e+" : "Animal Crossing",
431 | Comment2 = "NES Game:\n" + Header.Name,
432 | };
433 |
434 | BlankGCIFile.Header.FileName = (IsDnMe ? "DobutsunomoriE_F_" : "DobutsunomoriP_F_") + Header.Name.Substring(0, 4).ToUpper();
435 | BlankGCIFile.Header.GameCode = (IsDnMe ? "GAE" : "GAF") + RegionCodes[(int)GameRegion];
436 |
437 | byte[] GCIData = BlankGCIFile.GetData();
438 | SetChecksum(ref GCIData);
439 |
440 | return GCIData;
441 | }
442 | }
443 | }
444 |
--------------------------------------------------------------------------------
/ACNESCreator.Core/Patch.cs:
--------------------------------------------------------------------------------
1 | namespace ACNESCreator.Core
2 | {
3 | public static class Patch
4 | {
5 | public static readonly uint[] PatcherData = new uint[]
6 | {
7 | 0x9421FFF0, 0x7C0802A6, 0x90010014, 0x93E1000C, 0x7C7F1B78, 0x3C608000, 0x38633BAC, 0x4CC63182,
8 | 0x48056DC1, 0x3D20801F, 0x80896C64, 0x2F840000, 0x419E0060, 0x3C608000, 0x38633BC4, 0x4CC63182,
9 | 0x48056DA1, 0x3D20801F, 0x80696C64, 0x48000059, 0x3C608000, 0x38633C10, 0x4CC63182, 0x48056D85,
10 | 0x3D408063, 0x394AD4CC, 0x3D20806D, 0x39294B98, 0x91490004, 0x7FE3FB78, 0x48629AE5, 0x80010014,
11 | 0x7C0803A6, 0x83E1000C, 0x38210010, 0x4E800020, 0x3C608000, 0x38633BEC, 0x4CC63182, 0x48056D45,
12 | 0x4BFFFFB0, 0x9421FFD8, 0x7C0802A6, 0x9001002C, 0x9321000C, 0x93410010, 0x93610014, 0x93810018,
13 | 0x93A1001C, 0x93C10020, 0x93E10024, 0x7C7E1B78, 0xA3E30000, 0x7FE4FB78, 0x3C608000, 0x38633C28,
14 | 0x4CC63182, 0x48056CFD, 0x73E90001, 0x40820028, 0xA33E0002, 0x3BDE0004, 0x7F24CB78, 0x3C608000,
15 | 0x38633C70, 0x4CC63182, 0x48056CD9, 0x3BE00000, 0x4800005C, 0x3C608000, 0x38633C3C, 0x4CC63182,
16 | 0x48056CC1, 0x3D208040, 0x39494C68, 0x3D006000, 0x910A0018, 0x38800020, 0x80694C68, 0x48076201,
17 | 0x3C608000, 0x38633C64, 0x480736F9, 0x4BFFFFA4, 0x7FA4EB78, 0x3C608000, 0x38633CA0, 0x4CC63182,
18 | 0x48056C81, 0x3BFF0001, 0x57FF043E, 0x7F9FC840, 0x409C00A0, 0x83BE0000, 0x839E0004, 0x835E0008,
19 | 0x3B7E000C, 0x7FDBE214, 0x7FA6EB78, 0x7F85E378, 0x7FE4FB78, 0x3C608000, 0x38633C84, 0x4CC63182,
20 | 0x48056C41, 0x2F9D0000, 0x40BCFFA8, 0x7D3DE214, 0x3D40817F, 0x614AFFFF, 0x7F895040, 0x419DFF94,
21 | 0x7F85E378, 0x7F64DB78, 0x7FA3EB78, 0x4BFFF955, 0x7F84E378, 0x7FA3EB78, 0x48076165, 0x7F84E378,
22 | 0x7FA3EB78, 0x48076001, 0x73490001, 0x4182FF78, 0x7FA4EB78, 0x3C608000, 0x38633CB4, 0x4CC63182,
23 | 0x48056BE1, 0x7FA903A6, 0x4E800421, 0x4BFFFF58, 0x8001002C, 0x7C0803A6, 0x8321000C, 0x83410010,
24 | 0x83610014, 0x83810018, 0x83A1001C, 0x83C10020, 0x83E10024, 0x38210028, 0x4E800020, 0x4E455320,
25 | 0x54414720, 0x4C4F4144, 0x45522043, 0x414C4C45, 0x44000000, 0x6C6F6164, 0x696E6720, 0x66726F6D,
26 | 0x206E6573, 0x696E666F, 0x5F646174, 0x615F7374, 0x61727420, 0x40202570, 0x00000000, 0x4552524F,
27 | 0x523A206E, 0x6573696E, 0x666F5F64, 0x6174615F, 0x73746172, 0x74206973, 0x204E554C, 0x4C000000,
28 | 0x4E455320, 0x54414720, 0x4C4F4144, 0x45522045, 0x58495449, 0x4E470000, 0x676C6F62, 0x616C2066,
29 | 0x6C616773, 0x3A203078, 0x25780000, 0x656E6162, 0x6C696E67, 0x204A5554, 0x436F6E73, 0x6F6C6520,
30 | 0x77697468, 0x6F757420, 0x7A757275, 0x6D6F6465, 0x00000000, 0x54657374, 0x20726570, 0x6F727400,
31 | 0x6C6F6164, 0x696E6720, 0x25752070, 0x61746368, 0x65730000, 0x70617463, 0x68202325, 0x753A2030,
32 | 0x78257820, 0x62797465, 0x7320746F, 0x20257000, 0x696E7661, 0x6C696420, 0x74617267, 0x65742025,
33 | 0x70000000, 0x6A756D70, 0x20666C61, 0x67207365, 0x743B206A, 0x756D7069, 0x6E672074, 0x6F202570,
34 | 0x00000000
35 | };
36 |
37 | public const uint PatcherEntryPointData = 0x80003970;
38 | }
39 | }
--------------------------------------------------------------------------------
/ACNESCreator.Core/Utility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace ACNESCreator.Core
6 | {
7 | public static class Utility
8 | {
9 | public static byte[] GetPaddedData(byte[] Data, int MaxLength, byte PaddingValue = 0)
10 | {
11 | int Length = Data.Length;
12 | if (Length == MaxLength)
13 | {
14 | return Data;
15 | }
16 |
17 | Array.Resize(ref Data, MaxLength);
18 | if (PaddingValue != 0 && Length < MaxLength)
19 | {
20 | for (int i = Length; i < Data.Length; i++)
21 | {
22 | Data[i] = PaddingValue;
23 | }
24 | }
25 |
26 | return Data;
27 | }
28 |
29 | public static byte[] GetPaddedStringData(string Input, int MaxLength, byte PaddingValue = 0)
30 | => GetPaddedData(Encoding.ASCII.GetBytes(Input), MaxLength, PaddingValue);
31 |
32 | // From https://github.com/Daniel-McCarthy/Mr-Peeps-Compressor/blob/6c08b20c079fdb3e48400421f0385f02c5a55a05/PeepsCompress/PeepsCompress/Abstract%20Classes/SlidingWindowAlgorithm.cs
33 | public static int[] FindAllMatches(ref List dictionary, byte match)
34 | {
35 | List matchPositons = new List();
36 |
37 | for (int i = 0; i < dictionary.Count; i++)
38 | {
39 | if (dictionary[i] == match)
40 | {
41 | matchPositons.Add(i);
42 | }
43 | }
44 |
45 | return matchPositons.ToArray();
46 | }
47 |
48 | public static int[] FindLargestMatch(ref List dictionary, int[] matchesFound, ref byte[] file, int fileIndex, int maxMatch)
49 | {
50 | int[] matchSizes = new int[matchesFound.Length];
51 |
52 | for (int i = 0; i < matchesFound.Length; i++)
53 | {
54 | int matchSize = 1;
55 | bool matchFound = true;
56 |
57 | while (matchFound && matchSize < maxMatch && (fileIndex + matchSize < file.Length) && (matchesFound[i] + matchSize < dictionary.Count)) //NOTE: This could be relevant to compression issues? I suspect it's more related to writing
58 | {
59 | if (file[fileIndex + matchSize] == dictionary[matchesFound[i] + matchSize])
60 | {
61 | matchSize++;
62 | }
63 | else
64 | {
65 | matchFound = false;
66 | }
67 |
68 | }
69 |
70 | matchSizes[i] = matchSize;
71 | }
72 |
73 | int[] bestMatch = new int[2];
74 |
75 | bestMatch[0] = matchesFound[0];
76 | bestMatch[1] = matchSizes[0];
77 |
78 | for (int i = 1; i < matchesFound.Length; i++)
79 | {
80 | if (matchSizes[i] > bestMatch[1])
81 | {
82 | bestMatch[0] = matchesFound[i];
83 | bestMatch[1] = matchSizes[i];
84 | }
85 | }
86 |
87 | return bestMatch;
88 |
89 | }
90 |
91 | /* Adapted from: https://gist.github.com/infval/18d65dd034290fb908f589dcc10c6d25 */
92 | private static int FDSCRC(in byte[] data, int start, int end)
93 | {
94 | int s = 0x8000;
95 |
96 | // Process each byte in the data array
97 | for (; start < end; start++)
98 | {
99 | byte b = data[start];
100 | s |= b << 16;
101 | for (int i = 0; i < 8; i++)
102 | {
103 | if ((s & 1) != 0)
104 | {
105 | s ^= 0x8408 << 1;
106 | }
107 | s >>= 1;
108 | }
109 | }
110 |
111 | // Process two additional 0x00 bytes
112 | for (int j = 0; j < 2; j++)
113 | {
114 | for (int i = 0; i < 8; i++)
115 | {
116 | if ((s & 1) != 0)
117 | {
118 | s ^= 0x8408 << 1;
119 | }
120 | s >>= 1;
121 | }
122 | }
123 |
124 | return s;
125 | }
126 |
127 | private static void InsertCrc(in byte[] data, ref byte[] qdData, ref int qdWriteIdx, int start, int end)
128 | {
129 | int crc = FDSCRC(data, start, end);
130 | qdData[qdWriteIdx++] = (byte)(crc & 0xFF);
131 | qdData[qdWriteIdx++] = (byte)(crc >> 8);
132 | }
133 |
134 | public static byte[] ConvertFDSToQD(in byte[] disk)
135 | {
136 | int pos = 0;
137 |
138 | if (Encoding.ASCII.GetString(disk, 0, 3) == "FDS")
139 | {
140 | pos = 16; // skip FDS added to the beginning
141 | }
142 |
143 | if (disk[pos] != 0x01)
144 | {
145 | return Array.Empty();
146 | }
147 |
148 | byte[] qdData = new byte[0x10000];
149 | int qdWriteIdx = 0;
150 |
151 | Buffer.BlockCopy(disk, pos, qdData, qdWriteIdx, 0x38);
152 | qdWriteIdx += 0x38;
153 | InsertCrc(disk, ref qdData, ref qdWriteIdx, pos, pos + 0x38);
154 | Buffer.BlockCopy(disk, pos + 0x38, qdData, qdWriteIdx, 2);
155 | qdWriteIdx += 2;
156 | InsertCrc(disk, ref qdData, ref qdWriteIdx, pos + 0x38, pos + 0x3A);
157 | pos += 0x3A;
158 |
159 | try
160 | {
161 | while (disk[pos] == 3)
162 | {
163 | int fileSize = (disk[pos + 0xD]) | (disk[pos + 0xE] << 8);
164 | Buffer.BlockCopy(disk, pos, qdData, qdWriteIdx, 0x10);
165 | qdWriteIdx += 0x10;
166 | InsertCrc(disk, ref qdData, ref qdWriteIdx, pos, pos + 0x10);
167 | pos += 0x10;
168 | Buffer.BlockCopy(disk, pos, qdData, qdWriteIdx, fileSize + 1);
169 | qdWriteIdx += fileSize + 1;
170 | InsertCrc(disk, ref qdData, ref qdWriteIdx, pos, pos + 1 + fileSize);
171 | pos += 1 + fileSize;
172 | }
173 | }
174 | catch (ArgumentOutOfRangeException)
175 | {
176 | return Array.Empty();
177 | }
178 |
179 | return qdData;
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/ACNESCreator.Core/Yaz0.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ACNESCreator.Core
7 | {
8 | public static class Yaz0
9 | {
10 | ///
11 | /// Verifies that the supplied byte array is Yaz0 compressed.
12 | ///
13 | /// The Yaz0 compressed data array
14 | /// IsDataYaz0Compressed
15 | public static bool IsYaz0(byte[] Data)
16 | => Data.Length > 0x10 && Encoding.ASCII.GetString(Data, 0, 4).Equals("Yaz0");
17 |
18 | ///
19 | /// Decompresses Yaz0 compressed data.
20 | ///
21 | /// The Yaz0 compressed data array
22 | /// The decompressed data
23 | public static byte[] Decompress(byte[] Data)
24 | {
25 | if (!IsYaz0(Data))
26 | {
27 | throw new ArgumentException("The supplied data does not appear to be Yaz0 compressed!");
28 | }
29 |
30 | uint Size = (uint)(Data[4] << 24 | Data[5] << 16 | Data[6] << 8 | Data[7]);
31 | byte[] Output = new byte[Size];
32 | int ReadOffset = 16;
33 | int OutputOffset = 0;
34 |
35 | while (true)
36 | {
37 | byte Bitmap = Data[ReadOffset++];
38 | for (int i = 0; i < 8; i++)
39 | {
40 | if ((Bitmap & 0x80) != 0)
41 | {
42 | Output[OutputOffset++] = Data[ReadOffset++];
43 | }
44 | else
45 | {
46 | byte b = Data[ReadOffset++];
47 | int OffsetAdjustment = ((b & 0xF) << 8 | Data[ReadOffset++]) + 1;
48 | int Length = (b >> 4) + 2;
49 | if (Length == 2)
50 | {
51 | Length = Data[ReadOffset++] + 0x12;
52 | }
53 |
54 | for (int j = 0; j < Length; j++)
55 | {
56 | Output[OutputOffset] = Output[OutputOffset - OffsetAdjustment];
57 | OutputOffset++;
58 | }
59 | }
60 |
61 | Bitmap <<= 1;
62 |
63 | if (OutputOffset >= Size)
64 | {
65 | return Output;
66 | }
67 | }
68 | }
69 | }
70 |
71 | public static byte[] Compress(byte[] file)
72 | {
73 | List layoutBits = new List();
74 | List dictionary = new List();
75 |
76 | List uncompressedData = new List();
77 | List compressedData = new List();
78 |
79 | int maxDictionarySize = 4096;
80 | int minMatchLength = 3;
81 | int maxMatchLength = 255 + 0x12;
82 | int decompressedSize = 0;
83 |
84 | for (int i = 0; i < file.Length; i++)
85 | {
86 |
87 | if (dictionary.Contains(file[i]))
88 | {
89 | //compressed data
90 | int[] matches = Utility.FindAllMatches(ref dictionary, file[i]);
91 | int[] bestMatch = Utility.FindLargestMatch(ref dictionary, matches, ref file, i, maxMatchLength);
92 |
93 | if (bestMatch[1] >= minMatchLength)
94 | {
95 | layoutBits.Add(0);
96 | bestMatch[0] = dictionary.Count - bestMatch[0];
97 |
98 | for (int j = 0; j < bestMatch[1]; j++)
99 | {
100 | dictionary.Add(file[i + j]);
101 | }
102 |
103 | i = i + bestMatch[1] - 1;
104 |
105 | compressedData.Add(bestMatch);
106 | decompressedSize += bestMatch[1];
107 | }
108 | else
109 | {
110 | //uncompressed data
111 | layoutBits.Add(1);
112 | uncompressedData.Add(file[i]);
113 | dictionary.Add(file[i]);
114 | decompressedSize++;
115 | }
116 | }
117 | else
118 | {
119 | //uncompressed data
120 | layoutBits.Add(1);
121 | uncompressedData.Add(file[i]);
122 | dictionary.Add(file[i]);
123 | decompressedSize++;
124 | }
125 |
126 | if (dictionary.Count > maxDictionarySize)
127 | {
128 | int overflow = dictionary.Count - maxDictionarySize;
129 | dictionary.RemoveRange(0, overflow);
130 | }
131 | }
132 |
133 | return BuildYAZ0CompressedBlock(ref layoutBits, ref uncompressedData, ref compressedData, decompressedSize);
134 | }
135 |
136 | public static byte[] BuildYAZ0CompressedBlock(ref List layoutBits, ref List uncompressedData, ref List offsetLengthPairs, int decompressedSize)
137 | {
138 | List finalYAZ0Block = new List();
139 | List layoutBytes = new List();
140 | List compressedDataBytes = new List();
141 | List extendedLengthBytes = new List();
142 |
143 | //add Yaz0 magic number & decompressed file size
144 | finalYAZ0Block.AddRange(Encoding.ASCII.GetBytes("Yaz0"));
145 | finalYAZ0Block.AddRange(BitConverter.GetBytes(decompressedSize.Reverse()));
146 |
147 | //add 8 zeros per format specification
148 | for (int i = 0; i < 8; i++)
149 | {
150 | finalYAZ0Block.Add(0);
151 | }
152 |
153 | //assemble layout bytes
154 | while (layoutBits.Count > 0)
155 | {
156 | byte B = 0;
157 | for (int i = 0; i < (8 > layoutBits.Count ? layoutBits.Count : 8); i++)
158 | {
159 | B |= (byte)(layoutBits[i] << (7 - i));
160 | }
161 |
162 | layoutBytes.Add(B);
163 | layoutBits.RemoveRange(0, (layoutBits.Count < 8) ? layoutBits.Count : 8);
164 | }
165 |
166 | //assemble offsetLength shorts
167 | foreach (int[] offsetLengthPair in offsetLengthPairs)
168 | {
169 | //if < 18, set 4 bits -2 as matchLength
170 | //if >= 18, set matchLength == 0, write length to new byte - 0x12
171 |
172 | int adjustedOffset = offsetLengthPair[0];
173 | int adjustedLength = (offsetLengthPair[1] >= 18) ? 0 : offsetLengthPair[1] - 2; //vital, 4 bit range is 0-15. Number must be at least 3 (if 2, when -2 is done, it will think it is 3 byte format), -2 is how it can store up to 17 without an extra byte because +2 will be added on decompression
174 |
175 | if (adjustedLength == 0)
176 | {
177 | extendedLengthBytes.Add((byte)(offsetLengthPair[1] - 18));
178 | }
179 |
180 | int compressedInt = ((adjustedLength << 12) | adjustedOffset - 1);
181 |
182 | byte[] compressed2Byte = new byte[2];
183 | compressed2Byte[0] = (byte)(compressedInt & 0xFF);
184 | compressed2Byte[1] = (byte)((compressedInt >> 8) & 0xFF);
185 |
186 | compressedDataBytes.Add(compressed2Byte[1]);
187 | compressedDataBytes.Add(compressed2Byte[0]);
188 | }
189 |
190 | //add rest of file
191 | for (int i = 0; i < layoutBytes.Count; i++)
192 | {
193 | finalYAZ0Block.Add(layoutBytes[i]);
194 | byte LayoutByte = layoutBytes[i];
195 |
196 | for (int j = 7; ((j > -1) && ((uncompressedData.Count > 0) || (compressedDataBytes.Count > 0))); j--)
197 | {
198 | if (((LayoutByte >> j) & 1) == 1)
199 | {
200 | finalYAZ0Block.Add(uncompressedData[0]);
201 | uncompressedData.RemoveAt(0);
202 | }
203 | else
204 | {
205 | if (compressedDataBytes.Count > 0)
206 | {
207 | int length = compressedDataBytes[0] >> 4;
208 |
209 | finalYAZ0Block.Add(compressedDataBytes[0]);
210 | finalYAZ0Block.Add(compressedDataBytes[1]);
211 | compressedDataBytes.RemoveRange(0, 2);
212 |
213 | if (length == 0)
214 | {
215 | finalYAZ0Block.Add(extendedLengthBytes[0]);
216 | extendedLengthBytes.RemoveAt(0);
217 | }
218 | }
219 | }
220 | }
221 | }
222 |
223 | return finalYAZ0Block.ToArray();
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/ACNESCreator.FrontEnd.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {B5F81458-A03D-4D8C-9E64-00DD97E07105}
9 | WinExe
10 | ACNESCreator.FrontEnd
11 | ACNESCreator
12 | v4.8
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 | false
17 |
18 |
19 |
20 |
21 |
22 | AnyCPU
23 | true
24 | full
25 | false
26 | bin\Debug\
27 | DEBUG;TRACE
28 | prompt
29 | 4
30 |
31 |
32 | AnyCPU
33 | pdbonly
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 |
40 |
41 | famicom.ico
42 |
43 |
44 |
45 | ..\packages\Costura.Fody.3.3.3\lib\net40\Costura.dll
46 | True
47 |
48 |
49 | ..\ACNESCreator.Core\GCNToolKit.dll
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 4.0
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | MSBuild:Compile
69 | Designer
70 |
71 |
72 | MSBuild:Compile
73 | Designer
74 |
75 |
76 | App.xaml
77 | Code
78 |
79 |
80 | MainWindow.xaml
81 | Code
82 |
83 |
84 |
85 |
86 | Code
87 |
88 |
89 | True
90 | True
91 | Resources.resx
92 |
93 |
94 | True
95 | Settings.settings
96 | True
97 |
98 |
99 | ResXFileCodeGenerator
100 | Resources.Designer.cs
101 |
102 |
103 |
104 | SettingsSingleFileGenerator
105 | Settings.Designer.cs
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | {74b4224a-18a7-4d3d-96e0-1b2f82b28033}
117 | ACNESCreator.Core
118 |
119 |
120 |
121 |
122 |
123 |
124 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace ACNESCreator.FrontEnd
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
13 |
14 |
15 |
16 |
17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
18 |
19 |
20 |
21 |
22 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
23 |
24 |
25 |
26 |
27 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
28 |
29 |
30 |
31 |
32 | The order of preloaded assemblies, delimited with line breaks.
33 |
34 |
35 |
36 |
37 |
38 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
39 |
40 |
41 |
42 |
43 | Controls if .pdbs for reference assemblies are also embedded.
44 |
45 |
46 |
47 |
48 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
49 |
50 |
51 |
52 |
53 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
54 |
55 |
56 |
57 |
58 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
59 |
60 |
61 |
62 |
63 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
64 |
65 |
66 |
67 |
68 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
69 |
70 |
71 |
72 |
73 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
74 |
75 |
76 |
77 |
78 | A list of unmanaged 32 bit assembly names to include, delimited with |.
79 |
80 |
81 |
82 |
83 | A list of unmanaged 64 bit assembly names to include, delimited with |.
84 |
85 |
86 |
87 |
88 | The order of preloaded assemblies, delimited with |.
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
97 |
98 |
99 |
100 |
101 | A comma-separated list of error codes that can be safely ignored in assembly verification.
102 |
103 |
104 |
105 |
106 | 'false' to turn off automatic generation of the XML Schema file.
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System.Threading.Tasks;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.IO;
6 | using System.Windows.Media.Imaging;
7 | using System.Linq;
8 | using System;
9 | using System.Windows.Media;
10 | using System.Collections.Generic;
11 | using ACNESCreator.Core;
12 | using GCNToolKit.Formats.Images;
13 | using GCNToolKit.Formats.Colors;
14 |
15 | namespace ACNESCreator.FrontEnd
16 | {
17 | ///
18 | /// Interaction logic for MainWindow.xaml
19 | ///
20 | public partial class MainWindow : Window
21 | {
22 | readonly static string[] RegionCodes = new string[4] { "J", "E", "P", "U" };
23 | readonly OpenFileDialog SelectROMDialog = new OpenFileDialog
24 | {
25 | Filter = "All Supported Files|*.nes;*.qd;*.fds;*yaz0;*.bin|NES ROM Files|*.nes|Famicom Disk System Files (QD)|*.qd|Famicom Disk System Files (FDS)|*.fds|Yaz0 Compressed Files|*.yaz0|Binary Files|*.bin|All Files|*.*"
26 | };
27 | readonly OpenFileDialog SelectIconImageDialog = new OpenFileDialog
28 | {
29 | Filter = "PNG Files|*.png"
30 | };
31 |
32 | private bool _inProgress = false;
33 | private bool InProgress
34 | {
35 | get => _inProgress;
36 | set
37 | {
38 | GenerateButton.IsEnabled = !value;
39 | RegionComboBox.IsEnabled = !value;
40 | CanSaveCheckBox.IsEnabled = !value;
41 | GameNameTextBox.IsEnabled = !value;
42 | LocationTextBox.IsEnabled = !value;
43 | CompressCheckBox.IsEnabled = !value;
44 | BrowseButton.IsEnabled = !value;
45 | IsDnMe.IsEnabled = !value;
46 | ProgressBar.IsIndeterminate = value;
47 | _inProgress = value;
48 | }
49 | }
50 |
51 | private byte[] IconData;
52 |
53 | public MainWindow()
54 | {
55 | InitializeComponent();
56 |
57 | IconData = GCI.DefaultIconData;
58 | RefreshIconImage();
59 | }
60 |
61 | private void SetStatus(string status) => StatusLabel.Content = status;
62 |
63 | private void RefreshIconImage()
64 | {
65 | ushort[] Palette = new ushort[256];
66 | for (int i = 0; i < 256; i++)
67 | {
68 | Palette[i] = BitConverter.ToUInt16(IconData, 32 * 32 + i * 2).Reverse();
69 | }
70 |
71 | IconImage.Source = GetIconImage(IconData.Take(32 * 32).ToArray(), Palette);
72 | }
73 |
74 | private static BitmapSource GetIconImage(byte[] IconData, ushort[] Palette)
75 | {
76 | int[] IconImageData = C8.DecodeC8(IconData, Palette, 32, 32);
77 | byte[] IconImageDataArray = new byte[IconImageData.Length * 4];
78 | Buffer.BlockCopy(IconImageData, 0, IconImageDataArray, 0, IconImageDataArray.Length);
79 |
80 | return BitmapSource.Create(32, 32, 96, 96, PixelFormats.Bgra32, null, IconImageDataArray, 4 * 32);
81 | }
82 |
83 | private void BrowseButton_Click(object sender, RoutedEventArgs e)
84 | {
85 | if (SelectROMDialog.ShowDialog().Value)
86 | {
87 | if (File.Exists(SelectROMDialog.FileName))
88 | {
89 | LocationTextBox.Text = SelectROMDialog.FileName;
90 | }
91 | }
92 | }
93 |
94 | private async void GenerateButton_Click(object sender, RoutedEventArgs e)
95 | {
96 | if (!InProgress && !string.IsNullOrEmpty(GameNameTextBox.Text) && GameNameTextBox.Text.Length > 3 && !string.IsNullOrEmpty(LocationTextBox.Text)
97 | && File.Exists(LocationTextBox.Text))
98 | {
99 | InProgress = true;
100 | SetStatus("Creating GameCube GCI Save File...");
101 |
102 | string GameName = GameNameTextBox.Text;
103 | string ROMLocation = LocationTextBox.Text;
104 | bool HasSaveFile = CanSaveCheckBox.IsChecked.Value;
105 | Region ACRegion = (Region)RegionComboBox.SelectedIndex;
106 | bool DnMe = IsDnMe.IsChecked.Value;
107 |
108 | byte[] ROMData = null;
109 | try
110 | {
111 | ROMData = File.ReadAllBytes(ROMLocation);
112 | }
113 | catch
114 | {
115 | MessageBox.Show("The NES ROM File couldn't be read! Please make sure it isn't open in any other program, and double check the location!",
116 | "ROM File Read Error", MessageBoxButton.OK, MessageBoxImage.Error);
117 | InProgress = false;
118 | return;
119 | }
120 |
121 | // Force compression on DnM+ & DnMe+ since it's size offset is 0xC instead of 0x12. This ensures that the size is retrieved from the Yaz0 header.
122 | if (Yaz0.IsYaz0(ROMData) || ROMData.Length > NES.MaxROMSize)
123 | {
124 | CompressCheckBox.IsChecked = true;
125 | }
126 |
127 | bool Compress = !Yaz0.IsYaz0(ROMData) && CompressCheckBox.IsChecked.Value;
128 | NES NESFile = null;
129 | try
130 | {
131 | await Task.Run(() => { NESFile = new NES(GameName, ROMData, Path.GetExtension(ROMLocation), HasSaveFile, ACRegion, Compress, DnMe, IconData); });
132 | }
133 | catch (Exception ex)
134 | {
135 | MessageBox.Show("An error occured while generating the NES info! Please ensure that all your file location is correct, and try again!",
136 | "NES File Creation Error", MessageBoxButton.OK, MessageBoxImage.Error);
137 | InProgress = false;
138 | Console.WriteLine(ex.Message);
139 | Console.WriteLine(ex.StackTrace);
140 | return;
141 | }
142 |
143 | if (!NESFile.IsROM && !NESFile.IsFDS && !NESFile.IsQD && ACRegion == Region.NorthAmerica)
144 | {
145 | MessageBox.Show("Your file doesn't appear to be a NES ROM! It will be treated as a data patch, and will modify the game's memory instead!",
146 | "ROM Info", MessageBoxButton.OK, MessageBoxImage.Information);
147 | }
148 |
149 | var OutputLocation = Path.Combine(Path.GetDirectoryName(ROMLocation),
150 | $"{GameName}_{RegionCodes[(int)ACRegion]}_NESData.gci");
151 | try
152 | {
153 | var OutputData = NESFile.GenerateGCIFile();
154 | using (var Stream = new FileStream(OutputLocation, FileMode.Create))
155 | {
156 | Stream.Write(OutputData, 0, OutputData.Length);
157 | Stream.Flush();
158 | }
159 | SetStatus("Successfully created a save file containing the supplied ROM!");
160 | }
161 | catch (Exception ex)
162 | {
163 | MessageBox.Show("An error occured while saving the generated GCI file!", "GCI Creation Error", MessageBoxButton.OK, MessageBoxImage.Error);
164 | SetStatus("Failed to create a save file containing the supplied ROM!");
165 | InProgress = false;
166 | Console.WriteLine(ex.Message);
167 | Console.WriteLine(ex.StackTrace);
168 | return;
169 | }
170 |
171 | string msg;
172 |
173 | if (NESFile.IsFDS)
174 | {
175 | msg = $"The FDS ROM was successfully injected! Note that it was converted to QD format. The file is located here:\r\n{OutputLocation}";
176 | }
177 | else if (NESFile.IsQD || NESFile.IsROM)
178 | {
179 | msg = $"The {(NESFile.IsQD ? "QD" : "NES")} ROM was successfully injected! The file is located here:\r\n{OutputLocation}";
180 | }
181 | else
182 | {
183 | msg = $"The NES ROM was successfully injected! A rom type was unable to be determined, so it is assumed this is a ACE patch. The file is located here:\r\n{OutputLocation}";
184 | }
185 |
186 | MessageBox.Show(msg, "NES Save File Creation",
187 | MessageBoxButton.OK, MessageBoxImage.Information);
188 | InProgress = false;
189 | }
190 | }
191 |
192 | private void IsDnMe_Checked(object sender, RoutedEventArgs e)
193 | {
194 | RegionComboBox.IsEnabled = !IsDnMe.IsChecked.Value;
195 | RegionComboBox.SelectedIndex = 0;
196 | GameNameTextBox.MaxLength = RegionComboBox.IsEnabled ? 16 : 10;
197 | }
198 |
199 | private void Import_Click(object sender, RoutedEventArgs e)
200 | {
201 | if (SelectIconImageDialog.ShowDialog().Value && File.Exists(SelectIconImageDialog.FileName))
202 | {
203 | try
204 | {
205 | BitmapImage Img = new BitmapImage();
206 | Img.BeginInit();
207 | Img.UriSource = new Uri(SelectIconImageDialog.FileName);
208 | Img.EndInit();
209 |
210 | if (Img.PixelWidth == 32 && Img.PixelHeight == 32)
211 | {
212 | //IconImage.Source = Img;
213 |
214 | // Get the image data
215 | byte[] PixelData = new byte[4 * 32 * 32];
216 | Img.CopyPixels(PixelData, 4 * 32, 0);
217 |
218 | int[] ImageData = new int[32 * 32];
219 | Buffer.BlockCopy(PixelData, 0, ImageData, 0, PixelData.Length);
220 |
221 | // Convert it to C8 format
222 | IconData = new byte[0x600];
223 | List PaletteList = new List();
224 |
225 | for (int i = 0, idx = 0; i < 32 * 32; i++, idx += 4)
226 | {
227 | ushort RGB5A3Color = RGB5A3.ToRGB5A3(PixelData[idx + 3], PixelData[idx + 2], PixelData[idx + 1], PixelData[idx]);
228 | if (!PaletteList.Contains(RGB5A3Color))
229 | {
230 | PaletteList.Add(RGB5A3Color);
231 | }
232 | }
233 |
234 | ushort[] Palette = PaletteList.ToArray();
235 | if (Palette.Length > 256)
236 | {
237 | Array.Resize(ref Palette, 256);
238 | }
239 |
240 | C8.EncodeC8(ImageData, Palette, 32, 32).CopyTo(IconData, 0);
241 |
242 | for (int i = 0; i < PaletteList.Count; i++)
243 | {
244 | BitConverter.GetBytes(PaletteList[i].Reverse()).CopyTo(IconData, 0x400 + i * 2);
245 | }
246 |
247 | // Refresh the Icon Image
248 | RefreshIconImage();
249 | }
250 | else
251 | {
252 | MessageBox.Show("The icon you selected is not 32x32 pixels! Please resize it to that and try again.", "Icon Import Error", MessageBoxButton.OK,
253 | MessageBoxImage.Error);
254 | }
255 | }
256 | catch
257 | {
258 | MessageBox.Show("The icon you selected could not be imported! Please try again.", "Icon Import Error", MessageBoxButton.OK, MessageBoxImage.Error);
259 | }
260 | }
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("ACNESCreator")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("ACNESCreator")]
15 | [assembly: AssemblyCopyright("Copyright © 2018")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ACNESCreator.FrontEnd.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ACNESCreator.FrontEnd.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ACNESCreator.FrontEnd.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/Tags/TagInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace ACNESCreator.FrontEnd.Tags
4 | {
5 | public static class TagInfo
6 | {
7 | public struct TagDescription
8 | {
9 | public string Tag;
10 | public string Name;
11 | public string Description;
12 |
13 | public TagDescription(string tag, string name, string description)
14 | {
15 | Tag = tag;
16 | Name = name;
17 | Description = description;
18 | }
19 | }
20 |
21 | public static readonly List Descriptions = new List
22 | {
23 | new TagDescription("END", "End of Tags", "Causes the tag parser to terminate immediately. No further tags are processed."),
24 | new TagDescription("VEQ", "Variable Equal", "If the boolean argument is not set to true, the parser will skip the next tag."),
25 | new TagDescription("VNE", "Variable Not Equal", "If the boolean argument is set to true, the parser will skip the next tag."),
26 | new TagDescription("GID", "Game ID", "A string that represents the game. Can be anything. Used for generating a save name."),
27 | new TagDescription("GNM", "Game Name", "A string which is the title of the game. Can be anything."),
28 | new TagDescription("CPN", "Copy Name", "A string which is the title of the save game. Can be anything."),
29 | new TagDescription("OFS", "Offset", "Sets the offset into the save storage area in RAM. Same for both BBR and QDS."),
30 | new TagDescription("HSC", "High Score", "Sets the high score for a specific area in the game's save file."),
31 | new TagDescription("GNO", "Game Number", "Sets the game number. Used for internal games mainly."),
32 | new TagDescription("BBR", "Battery Backed RAM", "Marks a portion of the Battery Backup RAM area for saving."),
33 | new TagDescription("QDS", "Quick Disk Save", "Marks a protion of the Famicom Disk System save area for saving."),
34 | new TagDescription("SPE", "Special", "Calls a special subroutine for use on The Legend of Zelda. Modifies some of the game & calculates a checksum."),
35 | new TagDescription("TCS", "Tag Checksum", "Calculates the checksum for the tags. If the checksum + additive argument isn't equal to zero, the tags are considered invalid & the parser stops execution immediately."),
36 | new TagDescription("ICS", "Image Checksum", "Calculates the checksum for the ROM image. If the checksum + additive argument isn't equal to zero, the ROM image is considered corrupted."),
37 | new TagDescription("ESZ", "Expanded Size", "Sets the emulator's ROM expanded size variable."),
38 | new TagDescription("ROM", "Load ROM", "Loads another ROM from the internal ROMs. The current tags are used as the tags for that ROM."),
39 | new TagDescription("MOV", "Move Data", "Moves a section of data in the ROM from one location to another."),
40 | new TagDescription("NHD", "New Header Data", "Copies new header data to the ROM's header."),
41 | new TagDescription("DIF", "Difference", "Unused command. Acts like END and causes the parser to terminate execution immediately."),
42 | new TagDescription("PAT", "Patch Memory", "Allows a memory patch to be created. Writes data to an offset in RAM."),
43 | // TODO: Do we want to include completely unused tags like FIL, ISZ, IFM, and REM?
44 | };
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/famicom.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuyler36/ACNESCreator/167dd6f69e728ae64a01a8bc29f7f7c4d21bf586/ACNESCreator.FrontEnd/famicom.ico
--------------------------------------------------------------------------------
/ACNESCreator.FrontEnd/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ACNESCreator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28705.295
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACNESCreator.FrontEnd", "ACNESCreator.FrontEnd\ACNESCreator.FrontEnd.csproj", "{B5F81458-A03D-4D8C-9E64-00DD97E07105}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACNESCreator.CommandLine", "ACNESCreator.CommandLine\ACNESCreator.CommandLine.csproj", "{47D8A8DA-FD50-4222-A098-8E5BED4BA0D6}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACNESCreator.Core", "ACNESCreator.Core\ACNESCreator.Core.csproj", "{74B4224A-18A7-4D3D-96E0-1B2F82B28033}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {B5F81458-A03D-4D8C-9E64-00DD97E07105}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {B5F81458-A03D-4D8C-9E64-00DD97E07105}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {B5F81458-A03D-4D8C-9E64-00DD97E07105}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {B5F81458-A03D-4D8C-9E64-00DD97E07105}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {47D8A8DA-FD50-4222-A098-8E5BED4BA0D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {47D8A8DA-FD50-4222-A098-8E5BED4BA0D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {47D8A8DA-FD50-4222-A098-8E5BED4BA0D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {47D8A8DA-FD50-4222-A098-8E5BED4BA0D6}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {74B4224A-18A7-4D3D-96E0-1B2F82B28033}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {74B4224A-18A7-4D3D-96E0-1B2F82B28033}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {74B4224A-18A7-4D3D-96E0-1B2F82B28033}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {74B4224A-18A7-4D3D-96E0-1B2F82B28033}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {80AC2F84-2F3E-471D-861B-2E6E64D712DE}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ACNESCreator
2 | ACNESCreator allows you to create GameCube GCI save files with your favorite NES ROMs included in them to be played on Animal Crossing.
3 |
4 | ## Supported Titles
5 | * Dōbutsu no Mori+ (どうぶつの森+)
6 | * Animal Crossing
7 | * NTSC
8 | * PAL
9 | * Australia
10 | * Dōbutsu no Mori e+ (どうぶつの森e+)
11 |
12 | ## Features
13 | * Create save files with NES ROMs to play on the game-less NES in Animal Crossing
14 | * Create memory patches & code to be injected
15 |
16 | ## Creating a NES ROM Save File
17 | To create a NES ROM save file, open the program, give your ROM a name, browse for the NES ROM, and select your region.
18 | You can change the save file icon by right clicking on the current one and clicking "Import". 32x32 PNG is the supported format.
19 | Select any other options you wish to change, then click "Generate GCI File" to generate your save file.
20 | Once the save file has been created, import the save file to Dolphin, or use GCMM to get it on your physical memory card.
21 |
22 | ## Creating a Memory Patch
23 | By taking advantage of Animal Crossing's NES emulator's "Tag" settings, we can overwrite data anywhere in RAM with our own.
24 | First, you'll need to create a blank file.
25 | Then, you'll need to follow this structure format in that file:
26 | ```c
27 | struct AnimalCrossingNESPatchHeader {
28 | uint16_t GlobalFlags; // Global Loader Flags. Currently, setting the last flag will enable the JUTReportConsole without zurumode. [JUTConsoleEnabled = GlobalFlags & 1]
29 | uint16_t PatchCount; // Number of patches to copy.
30 | };
31 |
32 | struct AnimalCrossingNESPatch
33 | {
34 | uint32_t PatchAddress; // The location in memory to write data to.
35 | uint32_t PatchSize; // Size in bytes of patch data to copy to RAM.
36 | uint32_t PatchFlags; // Only the last flag used to mark the code as exectuable currently. [Executable = PatchFlags & 1]
37 | uint8_t Data[]; // The data to copy to RAM.
38 | } Patches[];
39 | ```
40 | You can find the source code of the loader [here](https://github.com/jamchamb/ac-patch-loader).
41 |
42 | After you've created your file, you should follow the same process as creating a NES ROM.
43 | The program will automatically detect that the file is a patch file and notify you of so.
44 |
45 | #### Special thanks to James Chambers for discovering the NES Memory Card loading functionality & the PAT tag.
46 |
--------------------------------------------------------------------------------