├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yaml
└── workflows
│ └── main.yml
├── .gitignore
├── AM2R Atomic.sln
├── Atomic.Gtk
├── Atomic.Gtk.csproj
└── Program.cs
├── Atomic.Mac
├── Atomic.Mac.csproj
├── Info.plist
├── MacIcon.icns
└── Program.cs
├── Atomic.Wpf
├── Atomic.Wpf.csproj
├── Program.cs
└── icon64.ico
├── Atomic
├── Atomic.csproj
├── Language
│ ├── Text.Designer.cs
│ ├── Text.de.resx
│ ├── Text.es.resx
│ ├── Text.fr.resx
│ ├── Text.it.resx
│ ├── Text.ja.resx
│ ├── Text.pt.resx
│ ├── Text.resx
│ ├── Text.ru.resx
│ └── Text.zh-Hans.resx
├── ModPacker.Designer.cs
├── ModPacker.cs
├── Resources.Designer.cs
├── Resources.resx
├── Resources
│ └── icon64.ico
└── SettingsForm.cs
├── AtomicLib
├── AtomicLib.csproj
├── Config.cs
├── Core.cs
├── FieldContents.cs
├── ModCreationInfo.cs
├── OS.cs
├── XML
│ ├── ModProfileXML.cs
│ └── Serializer.cs
└── utilities
│ ├── android
│ ├── LICENSE
│ └── apktool.jar
│ └── xdelta
│ ├── LICENSE
│ └── xdelta3.exe
├── AtomicLibTests
├── AtomicLibTests.csproj
├── CoreTests.cs
├── GameAndroid.apk
├── GameLin.zip
├── GameMac.zip
├── GameWin.zip
└── LICENSE-AM2RServer.txt
├── CHANGELOG.md
├── LICENSE
├── README.md
└── distribution
└── linux
├── Atomic.appdata.xml
├── Atomic.desktop
├── Atomic.png
└── screenshot.png
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Expected behavior**
14 | A clear and concise description of what you expected to happen.
15 |
16 | **To Reproduce**
17 | Steps to reproduce the behavior:
18 | 1.
19 | 2.
20 | 3.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Platform**
26 | - OS: [e.g. Windows 10]
27 | - Version: [e.g. 2.0.0]
28 |
29 | **Additional context**
30 | Add any other context about the problem here.
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "nuget" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 | labels:
13 | - "dependencies"
14 |
15 | groups:
16 | eto:
17 | patterns:
18 | - "Eto.*"
19 |
20 | - package-ecosystem: "github-actions"
21 | directory: "/"
22 | schedule:
23 | interval: "daily"
24 | labels:
25 | - "dependencies"
26 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Build and Upload
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - 'README.md'
7 | pull_request:
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-latest, macos-13, windows-latest]
16 | configuration: [Release]
17 | include:
18 | - os: ubuntu-latest
19 | COMMAND: Atomic.Gtk -p:PublishSingleFile=true -p:DebugType=embedded -r linux-x64 --no-self-contained -o builds/ubuntu-latest -p:BuildPlatform=Gtk
20 | ARTIFACT: builds/ubuntu-latest
21 | POSTBUILD: echo "nothing to do!"
22 | - os: macos-13
23 | COMMAND: Atomic.Mac -o builds/macOS-latest
24 | ARTIFACT: builds/macOS-latest
25 | POSTBUILD: rm -r builds/macOS-latest/* && mv Atomic.Mac/bin/Release/net8.0-macos/Atomic.Mac.app builds/macOS-latest/Atomic.Mac.app
26 | - os: windows-latest
27 | COMMAND: Atomic.Wpf -p:PublishSingleFile=true -p:DebugType=embedded -r win-x86 --no-self-contained -o builds/windows-latest
28 | ARTIFACT: builds/windows-latest
29 | POSTBUILD: echo "nothing to do!"
30 | runs-on: ${{ matrix.os }}
31 |
32 |
33 | steps:
34 | - uses: actions/checkout@v4
35 | - name: Setup .NET
36 | uses: actions/setup-dotnet@v4
37 | with:
38 | dotnet-version: 8.x.x
39 | - name: Install Mac workload
40 | working-directory: ./
41 | run: dotnet workload install macos && dotnet workload restore
42 | - name: Switch XCode
43 | run: sudo xcode-select -switch /Applications/Xcode_15.1.app/Contents/Developer
44 | if: matrix.os == 'macos-13'
45 | - name: Restore dependencies
46 | run: dotnet restore
47 | - name: Build
48 | run: dotnet publish ${{ matrix.COMMAND }} -c "${{ matrix.configuration }}"
49 | - name: Post-Build
50 | run: |
51 | cp ./LICENSE ./${{ matrix.ARTIFACT }}/
52 | ${{ matrix.POSTBUILD }}
53 | # Steps for uploading artifacts.
54 | - name: Zip to Archive
55 | run: 7z a -tzip ${{ matrix.os }}.zip ./${{ matrix.ARTIFACT }}/*
56 | - name: Upload Artifacts
57 | uses: actions/upload-artifact@v4.6.2
58 | with:
59 | name: ${{ matrix.os }}
60 | path: ${{ matrix.os }}.zip
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.vspscc
94 | *.vssscc
95 | .builds
96 | *.pidb
97 | *.svclog
98 | *.scc
99 |
100 | # Chutzpah Test files
101 | _Chutzpah*
102 |
103 | # Visual C++ cache files
104 | ipch/
105 | *.aps
106 | *.ncb
107 | *.opendb
108 | *.opensdf
109 | *.sdf
110 | *.cachefile
111 | *.VC.db
112 | *.VC.VC.opendb
113 |
114 | # Visual Studio profiler
115 | *.psess
116 | *.vsp
117 | *.vspx
118 | *.sap
119 |
120 | # Visual Studio Trace Files
121 | *.e2e
122 |
123 | # TFS 2012 Local Workspace
124 | $tf/
125 |
126 | # Guidance Automation Toolkit
127 | *.gpState
128 |
129 | # ReSharper is a .NET coding add-in
130 | _ReSharper*/
131 | *.[Rr]e[Ss]harper
132 | *.DotSettings.user
133 |
134 | # TeamCity is a build add-in
135 | _TeamCity*
136 |
137 | # DotCover is a Code Coverage Tool
138 | *.dotCover
139 |
140 | # AxoCover is a Code Coverage Tool
141 | .axoCover/*
142 | !.axoCover/settings.json
143 |
144 | # Coverlet is a free, cross platform Code Coverage Tool
145 | coverage*.json
146 | coverage*.xml
147 | coverage*.info
148 |
149 | # Visual Studio code coverage results
150 | *.coverage
151 | *.coveragexml
152 |
153 | # NCrunch
154 | _NCrunch_*
155 | .*crunch*.local.xml
156 | nCrunchTemp_*
157 |
158 | # MightyMoose
159 | *.mm.*
160 | AutoTest.Net/
161 |
162 | # Web workbench (sass)
163 | .sass-cache/
164 |
165 | # Installshield output folder
166 | [Ee]xpress/
167 |
168 | # DocProject is a documentation generator add-in
169 | DocProject/buildhelp/
170 | DocProject/Help/*.HxT
171 | DocProject/Help/*.HxC
172 | DocProject/Help/*.hhc
173 | DocProject/Help/*.hhk
174 | DocProject/Help/*.hhp
175 | DocProject/Help/Html2
176 | DocProject/Help/html
177 |
178 | # Click-Once directory
179 | publish/
180 |
181 | # Publish Web Output
182 | *.[Pp]ublish.xml
183 | *.azurePubxml
184 | # Note: Comment the next line if you want to checkin your web deploy settings,
185 | # but database connection strings (with potential passwords) will be unencrypted
186 | *.pubxml
187 | *.publishproj
188 |
189 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
190 | # checkin your Azure Web App publish settings, but sensitive information contained
191 | # in these scripts will be unencrypted
192 | PublishScripts/
193 |
194 | # NuGet Packages
195 | *.nupkg
196 | # NuGet Symbol Packages
197 | *.snupkg
198 | # The packages folder can be ignored because of Package Restore
199 | **/[Pp]ackages/*
200 | # except build/, which is used as an MSBuild target.
201 | !**/[Pp]ackages/build/
202 | # Uncomment if necessary however generally it will be regenerated when needed
203 | #!**/[Pp]ackages/repositories.config
204 | # NuGet v3's project.json files produces more ignorable files
205 | *.nuget.props
206 | *.nuget.targets
207 |
208 | # Microsoft Azure Build Output
209 | csx/
210 | *.build.csdef
211 |
212 | # Microsoft Azure Emulator
213 | ecf/
214 | rcf/
215 |
216 | # Windows Store app package directories and files
217 | AppPackages/
218 | BundleArtifacts/
219 | Package.StoreAssociation.xml
220 | _pkginfo.txt
221 | *.appx
222 | *.appxbundle
223 | *.appxupload
224 |
225 | # Visual Studio cache files
226 | # files ending in .cache can be ignored
227 | *.[Cc]ache
228 | # but keep track of directories ending in .cache
229 | !?*.[Cc]ache/
230 |
231 | # Others
232 | ClientBin/
233 | ~$*
234 | *~
235 | *.dbmdl
236 | *.dbproj.schemaview
237 | *.jfm
238 | *.pfx
239 | *.publishsettings
240 | orleans.codegen.cs
241 |
242 | # Including strong name files can present a security risk
243 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
244 | #*.snk
245 |
246 | # Since there are multiple workflows, uncomment next line to ignore bower_components
247 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
248 | #bower_components/
249 |
250 | # RIA/Silverlight projects
251 | Generated_Code/
252 |
253 | # Backup & report files from converting an old project file
254 | # to a newer Visual Studio version. Backup files are not needed,
255 | # because we have git ;-)
256 | _UpgradeReport_Files/
257 | Backup*/
258 | UpgradeLog*.XML
259 | UpgradeLog*.htm
260 | ServiceFabricBackup/
261 | *.rptproj.bak
262 |
263 | # SQL Server files
264 | *.mdf
265 | *.ldf
266 | *.ndf
267 |
268 | # Business Intelligence projects
269 | *.rdl.data
270 | *.bim.layout
271 | *.bim_*.settings
272 | *.rptproj.rsuser
273 | *- [Bb]ackup.rdl
274 | *- [Bb]ackup ([0-9]).rdl
275 | *- [Bb]ackup ([0-9][0-9]).rdl
276 |
277 | # Microsoft Fakes
278 | FakesAssemblies/
279 |
280 | # GhostDoc plugin setting file
281 | *.GhostDoc.xml
282 |
283 | # Node.js Tools for Visual Studio
284 | .ntvs_analysis.dat
285 | node_modules/
286 |
287 | # Visual Studio 6 build log
288 | *.plg
289 |
290 | # Visual Studio 6 workspace options file
291 | *.opt
292 |
293 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
294 | *.vbw
295 |
296 | # Visual Studio LightSwitch build output
297 | **/*.HTMLClient/GeneratedArtifacts
298 | **/*.DesktopClient/GeneratedArtifacts
299 | **/*.DesktopClient/ModelManifest.xml
300 | **/*.Server/GeneratedArtifacts
301 | **/*.Server/ModelManifest.xml
302 | _Pvt_Extensions
303 |
304 | # Paket dependency manager
305 | .paket/paket.exe
306 | paket-files/
307 |
308 | # FAKE - F# Make
309 | .fake/
310 |
311 | # CodeRush personal settings
312 | .cr/personal
313 |
314 | # Python Tools for Visual Studio (PTVS)
315 | __pycache__/
316 | *.pyc
317 |
318 | # Cake - Uncomment if you are using it
319 | # tools/**
320 | # !tools/packages.config
321 |
322 | # Tabs Studio
323 | *.tss
324 |
325 | # Telerik's JustMock configuration file
326 | *.jmconfig
327 |
328 | # BizTalk build output
329 | *.btp.cs
330 | *.btm.cs
331 | *.odx.cs
332 | *.xsd.cs
333 |
334 | # OpenCover UI analysis results
335 | OpenCover/
336 |
337 | # Azure Stream Analytics local run output
338 | ASALocalRun/
339 |
340 | # MSBuild Binary and Structured Log
341 | *.binlog
342 |
343 | # NVidia Nsight GPU debugger configuration file
344 | *.nvuser
345 |
346 | # MFractors (Xamarin productivity tool) working folder
347 | .mfractor/
348 |
349 | # Local History for Visual Studio
350 | .localhistory/
351 |
352 | # BeatPulse healthcheck temp database
353 | healthchecksdb
354 |
355 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
356 | MigrationBackup/
357 |
358 | # Ionide (cross platform F# VS Code tools) working folder
359 | .ionide/
360 |
361 | # Fody - auto-generated XML schema
362 | FodyWeavers.xsd
363 |
364 | # jetbrains rider
365 | .idea/
366 |
--------------------------------------------------------------------------------
/AM2R Atomic.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28803.156
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atomic", "Atomic\Atomic.csproj", "{13E58D3E-D34E-4354-B5D3-AC43B05E7212}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomicLib", "AtomicLib\AtomicLib.csproj", "{9FC86440-2F54-436C-A71C-50429D7CA0B9}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atomic.Wpf", "Atomic.Wpf\Atomic.Wpf.csproj", "{61CBA52B-16B2-4982-8046-BE3C730D8E45}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atomic.Gtk", "Atomic.Gtk\Atomic.Gtk.csproj", "{80D403DC-E4B6-45DF-ACC9-D70152AA8117}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atomic.Mac", "Atomic.Mac\Atomic.Mac.csproj", "{A0481858-CA50-4DB8-A547-193BD1D57D21}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomicLibTests", "AtomicLibTests\AtomicLibTests.csproj", "{705598A0-F06A-432C-869E-A98D480161FD}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {9FC86440-2F54-436C-A71C-50429D7CA0B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {9FC86440-2F54-436C-A71C-50429D7CA0B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {9FC86440-2F54-436C-A71C-50429D7CA0B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {9FC86440-2F54-436C-A71C-50429D7CA0B9}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {61CBA52B-16B2-4982-8046-BE3C730D8E45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {61CBA52B-16B2-4982-8046-BE3C730D8E45}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {61CBA52B-16B2-4982-8046-BE3C730D8E45}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {61CBA52B-16B2-4982-8046-BE3C730D8E45}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {80D403DC-E4B6-45DF-ACC9-D70152AA8117}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {80D403DC-E4B6-45DF-ACC9-D70152AA8117}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {80D403DC-E4B6-45DF-ACC9-D70152AA8117}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {80D403DC-E4B6-45DF-ACC9-D70152AA8117}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {A0481858-CA50-4DB8-A547-193BD1D57D21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {A0481858-CA50-4DB8-A547-193BD1D57D21}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {A0481858-CA50-4DB8-A547-193BD1D57D21}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {A0481858-CA50-4DB8-A547-193BD1D57D21}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {705598A0-F06A-432C-869E-A98D480161FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {705598A0-F06A-432C-869E-A98D480161FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {705598A0-F06A-432C-869E-A98D480161FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {705598A0-F06A-432C-869E-A98D480161FD}.Release|Any CPU.Build.0 = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {F2E9B4C2-3586-42C0-BAA3-616087857385}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/Atomic.Gtk/Atomic.Gtk.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | WinExe
7 | LatestMajor
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Atomic.Gtk/Program.cs:
--------------------------------------------------------------------------------
1 | using Eto.Forms;
2 |
3 | namespace Atomic.Gtk;
4 |
5 | class Program
6 | {
7 | [STAThread]
8 | static void Main(string[] args)
9 | {
10 | var application = new Application(Eto.Platforms.Gtk);
11 | application.UnhandledException += ApplicationOnUnhandledException;
12 | try
13 | {
14 | application.Run(new ModPacker());
15 | }
16 | catch (Exception e)
17 | {
18 | Console.WriteLine($"Unhandled Exception!\n*****Stack Trace*****\n\n{e}");
19 | }
20 |
21 | }
22 | private static void ApplicationOnUnhandledException(object sender, Eto.UnhandledExceptionEventArgs e)
23 | {
24 | Application.Instance.Invoke(() =>
25 | {
26 | MessageBox.Show($"Unhandled Exception!\n*****Stack Trace*****\n\n{e.ExceptionObject}", "GTK", MessageBoxType.Error);
27 | });
28 | }
29 | }
--------------------------------------------------------------------------------
/Atomic.Mac/Atomic.Mac.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-macos
5 | enable
6 | Exe
7 | osx-x64;osx-arm64
8 | 10.15
9 | LatestMajor
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Atomic.Mac/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleName
6 | Atomic
7 | CFBundleIdentifier
8 | io.github.am2r_community_developers.Atomic
9 | CFBundleShortVersionString
10 | 2.2.0
11 | LSMinimumSystemVersion
12 | 10.15
13 | CFBundleDevelopmentRegion
14 | en
15 | NSHumanReadableCopyright
16 |
17 | CFBundleIconFile
18 | MacIcon.icns
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Atomic.Mac/MacIcon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/Atomic.Mac/MacIcon.icns
--------------------------------------------------------------------------------
/Atomic.Mac/Program.cs:
--------------------------------------------------------------------------------
1 | using Eto.Forms;
2 |
3 | namespace Atomic.Mac;
4 |
5 | class Program
6 | {
7 | [STAThread]
8 | static void Main(string[] args)
9 | {
10 | var application = new Application(Eto.Platforms.macOS);
11 | application.UnhandledException += ApplicationOnUnhandledException;
12 | try
13 | {
14 | application.Run(new ModPacker());
15 | }
16 | catch (Exception e)
17 | {
18 | Console.WriteLine($"Unhandled Exception!\n*****Stack Trace*****\n\n{e}");
19 | }
20 |
21 | }
22 | private static void ApplicationOnUnhandledException(object sender, Eto.UnhandledExceptionEventArgs e)
23 | {
24 | Application.Instance.Invoke(() =>
25 | {
26 | MessageBox.Show($"Unhandled Exception!\n*****Stack Trace*****\n\n{e.ExceptionObject}", "GTK", MessageBoxType.Error);
27 | });
28 | }
29 | }
--------------------------------------------------------------------------------
/Atomic.Wpf/Atomic.Wpf.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows
5 | enable
6 | WinExe
7 | 10
8 | icon64.ico
9 | LatestMajor
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Atomic.Wpf/Program.cs:
--------------------------------------------------------------------------------
1 | using Eto.Forms;
2 |
3 | namespace Atomic.Wpf;
4 |
5 | class Program
6 | {
7 | [STAThread]
8 | static void Main(string[] args)
9 | {
10 | var application = new Application(Eto.Platforms.Wpf);
11 | application.UnhandledException += ApplicationOnUnhandledException;
12 | try
13 | {
14 | application.Run(new ModPacker());
15 | }
16 | catch (Exception e)
17 | {
18 | Console.WriteLine($"Unhandled Exception!\n*****Stack Trace*****\n\n{e}");
19 | }
20 |
21 | }
22 | private static void ApplicationOnUnhandledException(object sender, Eto.UnhandledExceptionEventArgs e)
23 | {
24 | Application.Instance.Invoke(() =>
25 | {
26 | MessageBox.Show($"Unhandled Exception!\n*****Stack Trace*****\n\n{e.ExceptionObject}", "GTK", MessageBoxType.Error);
27 | });
28 | }
29 | }
--------------------------------------------------------------------------------
/Atomic.Wpf/icon64.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/Atomic.Wpf/icon64.ico
--------------------------------------------------------------------------------
/Atomic/Atomic.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | 10
5 | LatestMajor
6 |
7 |
8 |
9 |
10 | Never
11 |
12 |
13 |
14 |
15 | Never
16 |
17 |
18 | Never
19 |
20 |
21 |
22 | Never
23 |
24 |
25 |
26 |
27 | Never
28 |
29 |
30 | PublicResXFileCodeGenerator
31 | Never
32 | Text.Designer.cs
33 |
34 |
35 | Never
36 |
37 |
38 |
39 | ResXFileCodeGenerator
40 | Resources.Designer.cs
41 |
42 |
43 |
44 |
45 |
46 | True
47 | True
48 | Resources.resx
49 |
50 |
51 | True
52 | True
53 | Text.resx
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 | namespace Atomic.Language {
11 | using System;
12 |
13 |
14 | [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
15 | [System.Diagnostics.DebuggerNonUserCodeAttribute()]
16 | [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
17 | public class Text {
18 |
19 | private static System.Resources.ResourceManager resourceMan;
20 |
21 | private static System.Globalization.CultureInfo resourceCulture;
22 |
23 | [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
24 | internal Text() {
25 | }
26 |
27 | [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
28 | public static System.Resources.ResourceManager ResourceManager {
29 | get {
30 | if (object.Equals(null, resourceMan)) {
31 | System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Atomic.Language.Text", typeof(Text).Assembly);
32 | resourceMan = temp;
33 | }
34 | return resourceMan;
35 | }
36 | }
37 |
38 | [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
39 | public static System.Globalization.CultureInfo Culture {
40 | get {
41 | return resourceCulture;
42 | }
43 | set {
44 | resourceCulture = value;
45 | }
46 | }
47 |
48 | public static string ModName {
49 | get {
50 | return ResourceManager.GetString("ModName", resourceCulture);
51 | }
52 | }
53 |
54 | public static string Author {
55 | get {
56 | return ResourceManager.GetString("Author", resourceCulture);
57 | }
58 | }
59 |
60 | public static string Version {
61 | get {
62 | return ResourceManager.GetString("Version", resourceCulture);
63 | }
64 | }
65 |
66 | public static string ModNotes {
67 | get {
68 | return ResourceManager.GetString("ModNotes", resourceCulture);
69 | }
70 | }
71 |
72 | public static string UsesCustomSaves {
73 | get {
74 | return ResourceManager.GetString("UsesCustomSaves", resourceCulture);
75 | }
76 | }
77 |
78 | public static string SelectFolder {
79 | get {
80 | return ResourceManager.GetString("SelectFolder", resourceCulture);
81 | }
82 | }
83 |
84 | public static string UsesCustomMusic {
85 | get {
86 | return ResourceManager.GetString("UsesCustomMusic", resourceCulture);
87 | }
88 | }
89 |
90 | public static string UsesYYC {
91 | get {
92 | return ResourceManager.GetString("UsesYYC", resourceCulture);
93 | }
94 | }
95 |
96 | public static string BundleAndroid {
97 | get {
98 | return ResourceManager.GetString("BundleAndroid", resourceCulture);
99 | }
100 | }
101 |
102 | public static string LoadAndroidAPK {
103 | get {
104 | return ResourceManager.GetString("LoadAndroidAPK", resourceCulture);
105 | }
106 | }
107 |
108 | public static string AndroidLoaded {
109 | get {
110 | return ResourceManager.GetString("AndroidLoaded", resourceCulture);
111 | }
112 | }
113 |
114 | public static string LoadAM2R11 {
115 | get {
116 | return ResourceManager.GetString("LoadAM2R11", resourceCulture);
117 | }
118 | }
119 |
120 | public static string AM2R11Loaded {
121 | get {
122 | return ResourceManager.GetString("AM2R11Loaded", resourceCulture);
123 | }
124 | }
125 |
126 | public static string CreateModPackage {
127 | get {
128 | return ResourceManager.GetString("CreateModPackage", resourceCulture);
129 | }
130 | }
131 |
132 | public static string ModPackageCreated {
133 | get {
134 | return ResourceManager.GetString("ModPackageCreated", resourceCulture);
135 | }
136 | }
137 |
138 | public static string ZipArchivesFileFilter {
139 | get {
140 | return ResourceManager.GetString("ZipArchivesFileFilter", resourceCulture);
141 | }
142 | }
143 |
144 | public static string APKFileFilter {
145 | get {
146 | return ResourceManager.GetString("APKFileFilter", resourceCulture);
147 | }
148 | }
149 |
150 | public static string InvalidSaveDirectory {
151 | get {
152 | return ResourceManager.GetString("InvalidSaveDirectory", resourceCulture);
153 | }
154 | }
155 |
156 | public static string YYCMacUnsupported {
157 | get {
158 | return ResourceManager.GetString("YYCMacUnsupported", resourceCulture);
159 | }
160 | }
161 |
162 | public static string Warning {
163 | get {
164 | return ResourceManager.GetString("Warning", resourceCulture);
165 | }
166 | }
167 |
168 | public static string SelectAM2R11FileDialog {
169 | get {
170 | return ResourceManager.GetString("SelectAM2R11FileDialog", resourceCulture);
171 | }
172 | }
173 |
174 | public static string FieldsMissing {
175 | get {
176 | return ResourceManager.GetString("FieldsMissing", resourceCulture);
177 | }
178 | }
179 |
180 | public static string Error {
181 | get {
182 | return ResourceManager.GetString("Error", resourceCulture);
183 | }
184 | }
185 |
186 | public static string NameInvalidCharacters {
187 | get {
188 | return ResourceManager.GetString("NameInvalidCharacters", resourceCulture);
189 | }
190 | }
191 |
192 | public static string AM2R11Invalid {
193 | get {
194 | return ResourceManager.GetString("AM2R11Invalid", resourceCulture);
195 | }
196 | }
197 |
198 | public static string PackagingMods {
199 | get {
200 | return ResourceManager.GetString("PackagingMods", resourceCulture);
201 | }
202 | }
203 |
204 | public static string ModPackagingAborted {
205 | get {
206 | return ResourceManager.GetString("ModPackagingAborted", resourceCulture);
207 | }
208 | }
209 |
210 | public static string ModdedGameNotFound {
211 | get {
212 | return ResourceManager.GetString("ModdedGameNotFound", resourceCulture);
213 | }
214 | }
215 |
216 | public static string Mac {
217 | get {
218 | return ResourceManager.GetString("Mac", resourceCulture);
219 | }
220 | }
221 |
222 | public static string Linux {
223 | get {
224 | return ResourceManager.GetString("Linux", resourceCulture);
225 | }
226 | }
227 |
228 | public static string Windows {
229 | get {
230 | return ResourceManager.GetString("Windows", resourceCulture);
231 | }
232 | }
233 |
234 | public static string ProfileXMLFound {
235 | get {
236 | return ResourceManager.GetString("ProfileXMLFound", resourceCulture);
237 | }
238 | }
239 |
240 | public static string SaveOSModProfile {
241 | get {
242 | return ResourceManager.GetString("SaveOSModProfile", resourceCulture);
243 | }
244 | }
245 |
246 | public static string Android {
247 | get {
248 | return ResourceManager.GetString("Android", resourceCulture);
249 | }
250 | }
251 |
252 | public static string SelectModdedFile {
253 | get {
254 | return ResourceManager.GetString("SelectModdedFile", resourceCulture);
255 | }
256 | }
257 |
258 | public static string APK {
259 | get {
260 | return ResourceManager.GetString("APK", resourceCulture);
261 | }
262 | }
263 |
264 | public static string Zip {
265 | get {
266 | return ResourceManager.GetString("Zip", resourceCulture);
267 | }
268 | }
269 |
270 | public static string OSGameLoaded {
271 | get {
272 | return ResourceManager.GetString("OSGameLoaded", resourceCulture);
273 | }
274 | }
275 |
276 | public static string SupportsOS {
277 | get {
278 | return ResourceManager.GetString("SupportsOS", resourceCulture);
279 | }
280 | }
281 |
282 | public static string LoadOSZip {
283 | get {
284 | return ResourceManager.GetString("LoadOSZip", resourceCulture);
285 | }
286 | }
287 |
288 | public static string SystemLanguage {
289 | get {
290 | return ResourceManager.GetString("SystemLanguage", resourceCulture);
291 | }
292 | }
293 |
294 | public static string LanguageNotice {
295 | get {
296 | return ResourceManager.GetString("LanguageNotice", resourceCulture);
297 | }
298 | }
299 |
300 | public static string SettingsTitle {
301 | get {
302 | return ResourceManager.GetString("SettingsTitle", resourceCulture);
303 | }
304 | }
305 |
306 | public static string SettingsMenu {
307 | get {
308 | return ResourceManager.GetString("SettingsMenu", resourceCulture);
309 | }
310 | }
311 |
312 | public static string RememberFields {
313 | get {
314 | return ResourceManager.GetString("RememberFields", resourceCulture);
315 | }
316 | }
317 |
318 | public static string QuitMenu {
319 | get {
320 | return ResourceManager.GetString("QuitMenu", resourceCulture);
321 | }
322 | }
323 |
324 | public static string FileMenu {
325 | get {
326 | return ResourceManager.GetString("FileMenu", resourceCulture);
327 | }
328 | }
329 | }
330 | }
331 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.de.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Mod-Name:
23 |
24 |
25 | Autor(en):
26 |
27 |
28 | Version:
29 |
30 |
31 | Mod-Notizen:
32 |
33 |
34 | Verwendet eigenes Speicherverzeichnis
35 |
36 |
37 | Ordner auswhählen
38 |
39 |
40 | Verwendet eigene Musik
41 |
42 |
43 | Verwendet YoYo Compiler
44 |
45 |
46 | Zusätzlich Android unterstützen
47 |
48 |
49 | Lade modifizierte Android APK
50 |
51 |
52 | Modifizierte APK geladen!
53 |
54 |
55 | Lade 1.1
56 |
57 |
58 | 1.1 geladen!
59 |
60 |
61 | Mod-Paket(e) erstellen
62 |
63 |
64 | Mod-Paket erstellt!
65 |
66 |
67 | Zip-Archive (*.zip)
68 |
69 |
70 | Android-Anwendungspakete (*.apk)
71 |
72 |
73 | Ungültiges Speicherverzeichnis! Bitte konsultieren Sie die GameMaker: Studio 1.4 Dokumentation für gültige Speicherverzeichnisse!
74 |
75 |
76 | YoYoCompiler wird mit Mac nicht unterstützt!
77 |
78 |
79 | Warnung
80 |
81 |
82 | AM2R_1.1.zip auswählen
83 |
84 |
85 | Mod-Name, Autor oder Versions fehlen! Mod erstellung abgebrochen.
86 |
87 |
88 | Fehler
89 |
90 |
91 | Name enthält ungültige Zeichen! Diese Zeichen sind nicht erlaubt:
92 |
93 |
94 | AM2R 1.1 zip ist ungültig! Fehlercode:
95 |
96 |
97 | Mod-Paket(e) werden erstellt... Das könnte eine Weile dauern!
98 |
99 |
100 | Mod erstellung abgebrochen!
101 |
102 |
103 | Modifiziertes {0} Spiel nicht gefunden, vergewissern Sie sich, dass es sich nicht in einem Unterordner befindet.\nDas erstellte Profil ist wahrscheinlich nicht installierbar, sind Sie sicher, dass Sie fortfahren möchten?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Windows
113 |
114 |
115 | profile.xml gefunden. Diese Datei wird vom AM2RLauncher verwendet, um Profilinformationen zu bestimmen, und ihr Einschluss kann dazu führen, dass das Profil nicht installiert werden kann. Sind Sie sicher, dass Sie fortfahren möchten?
116 |
117 |
118 | Mod-Profil {0} speichern
119 |
120 |
121 | Android
122 |
123 |
124 | Bitte wählen Sie Ihr modifiziertes {0} AM2R .{1}
125 |
126 |
127 | apk
128 |
129 |
130 | zip
131 |
132 |
133 | Modifizertes {0} spiel geladen!
134 |
135 |
136 | Unterstützt {0}
137 |
138 |
139 | Lade modifizierte {0} .zip
140 |
141 |
142 | &Datei
143 |
144 |
145 | &Beenden
146 |
147 |
148 | Automatisches Ausfüllen von Feldern aus der letzten Verwendung
149 |
150 |
151 | &Einstellungen
152 |
153 |
154 | Einstellungen
155 |
156 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.es.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Nombre del mod:
23 |
24 |
25 | Autor(es):
26 |
27 |
28 | Versión:
29 |
30 |
31 | Notas del mod:
32 |
33 |
34 | Utiliza directorio para archivos de guardado personalizado
35 |
36 |
37 | Seleccionar carpeta
38 |
39 |
40 | Utiliza música personalizada
41 |
42 |
43 | Utiliza YoYo Compiler
44 |
45 |
46 | Añadir archivos para Android
47 |
48 |
49 | Cargar archivo APK para Android modificado
50 |
51 |
52 | ¡APK modificado cargado!
53 |
54 |
55 | Cargar 1.1
56 |
57 |
58 | ¡Versión 1.1 cargada!
59 |
60 |
61 | Crear paquete(s) del mod
62 |
63 |
64 | ¡Paquete del mod creado!
65 |
66 |
67 | Archivos zip (*.zip)
68 |
69 |
70 | Paquetes de instalación de Android (*.apk)
71 |
72 |
73 | ¡Directorio para archivos de guardado no válido! ¡Por favor, consulte los directorios válidos en los documentos de GameMaker: Studio 1.4!
74 |
75 |
76 | ¡YoYo Compiler no funciona en Mac!
77 |
78 |
79 | Advertencia
80 |
81 |
82 | Selecciona AM2R_1.1.zip
83 |
84 |
85 | ¡Campo de nombre del mod, autor o versión vacío! Proceso cancelado.
86 |
87 |
88 | Error
89 |
90 |
91 | ¡El nombre contiene caracteres no válidos! Estos caracteres no están permitidos:
92 |
93 |
94 | ¡El zip de AM2R 1.1 no es válido! Código de error:
95 |
96 |
97 | Empaquetando mod(s)… ¡Esto puede tardar un rato!
98 |
99 |
100 | ¡Paquete del mod cancelado!
101 |
102 |
103 | No se encuentra el juego modificado para {0}. Asegúrese de que no se encuentra en una subcarpeta. \nEl perfil creado probablemente no pueda ser instalado. ¿Desea continuar?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Windows
113 |
114 |
115 | profile.xml detectado. AM2RLauncher utiliza este archivo para determinar estadísticas del perfil y su inclusión podría hacer que el perfil no pueda ser instalado. ¿Desea continuar?
116 |
117 |
118 | Guardar perfil del mod para {0}
119 |
120 |
121 | Android
122 |
123 |
124 | Por favor, seleccione su archivo .{1} modificado de AM2R para {0}
125 |
126 |
127 | apk
128 |
129 |
130 | zip
131 |
132 |
133 | ¡Juego modificado para {0} cargado!
134 |
135 |
136 | Funciona en {0}
137 |
138 |
139 | Cargar archivo .zip para {0} modificado
140 |
141 |
142 | Opciones
143 |
144 |
145 | &Opciones
146 |
147 |
148 | &Salir
149 |
150 |
151 | Rellenar los campos automáticamente con los valores utilizados en la última sesión
152 |
153 |
154 | &Archivo
155 |
156 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.fr.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Nom de mod:
23 |
24 |
25 | Auteur(s) :
26 |
27 |
28 | Version:
29 |
30 |
31 | Notes de mod:
32 |
33 |
34 | Utilise un répertoire de sauvegarde personnalisé
35 |
36 |
37 | Sélectionner le dossier
38 |
39 |
40 | Utilise des musiques personnalisées
41 |
42 |
43 | Utilise YoYo Compiler
44 |
45 |
46 | Supporte Android
47 |
48 |
49 | Charger le Android moddé fichier APK
50 |
51 |
52 | APK moddée chargée!
53 |
54 |
55 | Charger la 1.1
56 |
57 |
58 | 1.1 chargé!
59 |
60 |
61 | Créer archive(s) de mods
62 |
63 |
64 | Archive de mod créée!
65 |
66 |
67 | Archives Zip (*.zip)
68 |
69 |
70 | Archive application Android (*.apk)
71 |
72 |
73 | Dossier de sauvegarde invalide! Se réferrer à la doc de Gamemaker: Studio 1.4 pour les répertoires valides!
74 |
75 |
76 | YoYo Compiler n’est pas supporté sur Mac!
77 |
78 |
79 | Avertissement
80 |
81 |
82 | Sélectionner AM2R_1.1.zip
83 |
84 |
85 | Nom, auteur ou version de mod manquante! Archivage stoppé.
86 |
87 |
88 | Erreur
89 |
90 |
91 | Le nom contient des caractères invalides! Ces caractères ne sont pas utilisables:
92 |
93 |
94 | zip de AM2R 1.1 invalide! Code d’erreur:
95 |
96 |
97 | En train d’archiver le(s) mod(s)… ça peut prendre un moment!
98 |
99 |
100 | Archivage de mod arrêté!
101 |
102 |
103 | Jeu {0} moddé non trouvé, soyez sûr qu’il ne soit pas dans un sous-dossier.\nLe profil crée ne sera sûrement pas installable, êtes-vous sûr de vouloir continuer?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Windows
113 |
114 |
115 | profile.xml trouvé. Ce fichier est utilisé par AM2RLauncher pour déterminer les stats de profil et son inclusion pourrait rendre le profil non désinstallable. Êtes-vous sûr de vouloir continuer?
116 |
117 |
118 | Sauvegarder le profil de mod {0}
119 |
120 |
121 | Android
122 |
123 |
124 | Veuiller sélectionner votre AM2R $1 moddé. Fichier $2
125 |
126 |
127 | apk
128 |
129 |
130 | zip
131 |
132 |
133 | Jeu {0} moddé chargé!
134 |
135 |
136 | Supporte {0}
137 |
138 |
139 | Charger le {0} moddé fichier .zip
140 |
141 |
142 | &Fichier
143 |
144 |
145 | &Quitter
146 |
147 |
148 | Remplir automatiquement selon les derniers paramètres
149 |
150 |
151 | &Préférences
152 |
153 |
154 | Préférences
155 |
156 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.it.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Mod name:
23 |
24 |
25 | Autore/i:
26 |
27 |
28 | Version:
29 |
30 |
31 | Mod notes:
32 |
33 |
34 | Uses custom save directory
35 |
36 |
37 | Select folder
38 |
39 |
40 | Uses custom music
41 |
42 |
43 | Uses YoYo Compiler
44 |
45 |
46 | Additionally bundle Android
47 |
48 |
49 | Load modded Android APK
50 |
51 |
52 | Modded APK loaded!
53 |
54 |
55 | Load 1.1
56 |
57 |
58 | 1.1 loaded!
59 |
60 |
61 | Create mod package(s)
62 |
63 |
64 | Mod package created!
65 |
66 |
67 | Archivi Zip (.zip)
68 |
69 |
70 | Android application packages (*.apk)
71 |
72 |
73 | Invalid save directory! Please consult the GameMaker: Studio 1.4 documentation for valid save directories!
74 |
75 |
76 | YoYoCompiler isn't supported with Mac!
77 |
78 |
79 | Warning
80 |
81 |
82 | Seleziona AM2R_1.1.zip
83 |
84 |
85 | Mod name, author or version field missing! Mod packaging aborted.
86 |
87 |
88 | Errore
89 |
90 |
91 | Name contains invalid characters! These characters are not allowed:
92 |
93 |
94 | AM2R 1.1 zip is invalid! Error code:
95 |
96 |
97 | Packaging mod(s)... This could take a while!
98 |
99 |
100 | Mod packaging aborted!
101 |
102 |
103 | Modded {0} game not found, make sure it's not placed in any subfolders.\nCreated profile will likely not be installable, are you sure you want to continue?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Windows
113 |
114 |
115 | profile.xml found. This file is used by the AM2RLauncher to determine profile stats and its inclusion may make the profile uninstallable. Are you sure you want to continue?
116 |
117 |
118 | Save {0} mod profile
119 |
120 |
121 | Android
122 |
123 |
124 | Please select your modded {0} AM2R .{1}
125 |
126 |
127 | apk
128 |
129 |
130 | zip
131 |
132 |
133 | Modded {0} game loaded!
134 |
135 |
136 | Supports {0}
137 |
138 |
139 | Load modded {0} .zip
140 |
141 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.ja.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | MOD名:
23 |
24 |
25 | 制作者:
26 |
27 |
28 | バージョン:
29 |
30 |
31 | MOD詳細:
32 |
33 |
34 | 他のセーブフォルダを使用
35 |
36 |
37 | フォルダを選択してください
38 |
39 |
40 | カスタムBGMを使用
41 |
42 |
43 | YoYoコンパイラを使用
44 |
45 |
46 | Androidも含める
47 |
48 |
49 | MODを適用した Android の APK ファイルをロード
50 |
51 |
52 | MODを適用したAPKをロードしました
53 |
54 |
55 | 1.1 をロード
56 |
57 |
58 | 1.1をロードしました
59 |
60 |
61 | MODをパッケージ化する
62 |
63 |
64 | MODをパッケージ化しました
65 |
66 |
67 | ZIPファイル (*.zip)
68 |
69 |
70 | Android アプリパッケージ (*.apk)
71 |
72 |
73 | セーブフォルダが無効です! 有効なディレクトリについては、GameMaker: Studio 1.4 のドキュメントを参照してください
74 |
75 |
76 | YoYoコンパイラはMacには対応していません
77 |
78 |
79 | 注意
80 |
81 |
82 | AM2R_1.1.zip を選択してください
83 |
84 |
85 | MOD名か、制作者か、またはバージョンのフィールドがありません! MODのパッケージ化を中断します
86 |
87 |
88 | エラー
89 |
90 |
91 | 名前に無効な文字が含まれています! 次の文字は使用できません:
92 |
93 |
94 | AM2R 1.1 zip は無効です! エラーコード:
95 |
96 |
97 | MODをパッケージ化します...これには時間がかかる場合があります
98 |
99 |
100 | MODのパッケージ化が中断されました
101 |
102 |
103 | MODを適用した {0} のゲームが見つかりません。サブフォルダにないか確認してください。\n作成したプロファイルはインストールできない可能性があります。続行しますか?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Windows
113 |
114 |
115 | profile.xml が見つかりました。このファイルはAM2RLauncherがプロファイルのステータスを決定するために使用するため、含まれているとアンインストールできなくなる可能性があります。続行しますか?
116 |
117 |
118 | {0} のMODプロファイルを保存
119 |
120 |
121 | Android
122 |
123 |
124 | MODを適用した {0} の AM2R.{1} ファイルを選択してください
125 |
126 |
127 | apk
128 |
129 |
130 | zip
131 |
132 |
133 | MODを適用した {0} のゲームをロードしました
134 |
135 |
136 | {0} 対応
137 |
138 |
139 | MODを適用した {0} の .zip ファイルをロード
140 |
141 |
142 | &ファイル
143 |
144 |
145 | &終了
146 |
147 |
148 | 前回使用時のフィールドを自動入力する
149 |
150 |
151 | &設定
152 |
153 |
154 | 設定
155 |
156 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.pt.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Mod name:
23 |
24 |
25 | Autor(es):
26 |
27 |
28 | Version:
29 |
30 |
31 | Mod notes:
32 |
33 |
34 | Uses custom save directory
35 |
36 |
37 | Select folder
38 |
39 |
40 | Uses custom music
41 |
42 |
43 | Uses YoYo Compiler
44 |
45 |
46 | Additionally bundle Android
47 |
48 |
49 | Load modded Android APK
50 |
51 |
52 | Modded APK loaded!
53 |
54 |
55 | Load 1.1
56 |
57 |
58 | 1.1 loaded!
59 |
60 |
61 | Create mod package(s)
62 |
63 |
64 | Mod package created!
65 |
66 |
67 | Arquivos Zip (*.zip)
68 |
69 |
70 | Android application packages (*.apk)
71 |
72 |
73 | Invalid save directory! Please consult the GameMaker: Studio 1.4 documentation for valid save directories!
74 |
75 |
76 | YoYoCompiler isn't supported with Mac!
77 |
78 |
79 | Warning
80 |
81 |
82 | Selecione o AM2R_1.1.zip
83 |
84 |
85 | Mod name, author or version field missing! Mod packaging aborted.
86 |
87 |
88 | Erro
89 |
90 |
91 | Name contains invalid characters! These characters are not allowed:
92 |
93 |
94 | AM2R 1.1 zip is invalid! Error code:
95 |
96 |
97 | Packaging mod(s)... This could take a while!
98 |
99 |
100 | Mod packaging aborted!
101 |
102 |
103 | Modded {0} game not found, make sure it's not placed in any subfolders.\nCreated profile will likely not be installable, are you sure you want to continue?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Windows
113 |
114 |
115 | profile.xml found. This file is used by the AM2RLauncher to determine profile stats and its inclusion may make the profile uninstallable. Are you sure you want to continue?
116 |
117 |
118 | Save {0} mod profile
119 |
120 |
121 | Android
122 |
123 |
124 | Please select your modded {0} AM2R .{1}
125 |
126 |
127 | apk
128 |
129 |
130 | zip
131 |
132 |
133 | Modded {0} game loaded!
134 |
135 |
136 | Supports {0}
137 |
138 |
139 | Load modded {0} .zip
140 |
141 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Mod name:
23 |
24 |
25 | Author(s):
26 |
27 |
28 | Version:
29 |
30 |
31 | Mod notes:
32 |
33 |
34 | Uses custom save directory
35 |
36 |
37 | Select folder
38 |
39 |
40 | Uses custom music
41 |
42 |
43 | Uses YoYo Compiler
44 |
45 |
46 | Additionally bundle Android
47 |
48 |
49 | Load modded Android APK
50 |
51 |
52 | Modded APK loaded!
53 |
54 |
55 | Load 1.1
56 |
57 |
58 | 1.1 loaded!
59 |
60 |
61 | Create mod package(s)
62 |
63 |
64 | Mod package created!
65 |
66 |
67 | Zip Archives (*.zip)
68 |
69 |
70 | Android application packages (*.apk)
71 |
72 |
73 | Invalid save directory! Please consult the GameMaker: Studio 1.4 documentation for valid save directories!
74 |
75 |
76 | YoYoCompiler isn't supported with Mac!
77 |
78 |
79 | Warning
80 |
81 |
82 | Select AM2R_1.1.zip
83 |
84 |
85 | Mod name, author or version field missing! Mod packaging aborted.
86 |
87 |
88 | Error
89 |
90 |
91 | Name contains invalid characters! These characters are not allowed:
92 |
93 |
94 | AM2R 1.1 zip is invalid! Error code:
95 |
96 |
97 | Packaging mod(s)... This could take a while!
98 |
99 |
100 | Mod packaging aborted!
101 |
102 |
103 | Modded {0} game not found, make sure it's not placed in any subfolders.\nCreated profile will likely not be installable, are you sure you want to continue?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Windows
113 |
114 |
115 | profile.xml found. This file is used by the AM2RLauncher to determine profile stats and its inclusion may make the profile uninstallable. Are you sure you want to continue?
116 |
117 |
118 | Save {0} mod profile
119 |
120 |
121 | Android
122 |
123 |
124 | Please select your modded {0} AM2R .{1}
125 |
126 |
127 | apk
128 |
129 |
130 | zip
131 |
132 |
133 | Modded {0} game loaded!
134 |
135 |
136 | Supports {0}
137 |
138 |
139 | Load modded {0} .zip
140 |
141 |
142 | System Language
143 |
144 |
145 | Language (change requires a restart)
146 |
147 |
148 | Settings
149 |
150 |
151 | &Settings
152 |
153 |
154 | Automatically fill in fields from last usage
155 |
156 |
157 | &Quit
158 |
159 |
160 | &File
161 |
162 |
163 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.ru.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Название модификации:
23 |
24 |
25 | Автор(ы):
26 |
27 |
28 | Версия:
29 |
30 |
31 | Заметки:
32 |
33 |
34 | Настроить пользовательскую папку сохранений
35 |
36 |
37 | Выберите папку
38 |
39 |
40 | Использовать пользовательскую музыку
41 |
42 |
43 | Использует компилятор YoYo
44 |
45 |
46 | Включить поддержку Android
47 |
48 |
49 | Загрузить модифицированный APK файл для Android
50 |
51 |
52 | Модифицированный APK-файл загружен!
53 |
54 |
55 | Загрузить версию 1.1
56 |
57 |
58 | Версия 1.1 загружена!
59 |
60 |
61 | Создать пакет(ы) модификаций
62 |
63 |
64 | Пакет модификаций создан!
65 |
66 |
67 | Архивы ZIP (*.zip)
68 |
69 |
70 | Пакеты приложений для Android (*.apk)
71 |
72 |
73 | Недопустимая папка сохранений! Пожалуйста, обратитесь за дополнительной информацией о допустимых папках сохранений к документации GameMaker Studio 1.4.
74 |
75 |
76 | Компилятор YoYo не поддерживается Mac!
77 |
78 |
79 | Предупреждение
80 |
81 |
82 | Выберите AM2R_1.1.zip
83 |
84 |
85 | Не заполнено поле названия, автора или версии модификации! Упаковка модификации отменена.
86 |
87 |
88 | Ошибка
89 |
90 |
91 | В названии содержатся недопустимые символы! Запрещены следующие символы:
92 |
93 |
94 | Архив AM2R 1.1 не валиден. Код ошибки:
95 |
96 |
97 | Упаковка модификаций… Это может занять некоторое время. Может, сделаете себе чай?
98 |
99 |
100 | Упаковка модификаций отменена!
101 |
102 |
103 | Модифицированная игра {0} не найдена, убедитесь что она находится в корневой папке.\nСозданный профиль скорее всего не получится установить. Все равно продолжить?
104 |
105 |
106 | Mac
107 |
108 |
109 | Linux
110 |
111 |
112 | Обнаружен profile.xml. Этот файл используется лаунчером AM2R для определения статуса профиля, включение этого файла в пакет может сделать профиль нерабочим. Все равно продолжить?
113 |
114 |
115 | Сохранить профиль модификаций для {0}
116 |
117 |
118 | Android
119 |
120 |
121 | Пожалуйста, выберите свой модифицированный AM2R .{1} файл для {0}
122 |
123 |
124 | APK
125 |
126 |
127 | zip
128 |
129 |
130 | Модифицированная игра {0} загружена!
131 |
132 |
133 | Поддерживает {0}
134 |
135 |
136 | Загрузить модифицированный .zip файл для {0}
137 |
138 |
139 | &Файл
140 |
141 |
142 | &Выйти
143 |
144 |
145 | Автоматически заполнять поля данными с предыдущего запуска
146 |
147 |
148 | &Настройки
149 |
150 |
151 | Настройки
152 |
153 |
154 | Windows
155 |
156 |
--------------------------------------------------------------------------------
/Atomic/Language/Text.zh-Hans.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Mod 名称:
23 |
24 |
25 | 作者:
26 |
27 |
28 | 版本:
29 |
30 |
31 | Mod 说明:
32 |
33 |
34 | 使用自定义的保存数据文件夹
35 |
36 |
37 | 选择文件夹
38 |
39 |
40 | 使用自定义音乐
41 |
42 |
43 | 使用 YoYo Compiler
44 |
45 |
46 | 额外附带 Android 版
47 |
48 |
49 | 加载修改的 Android 版的 APK 文件
50 |
51 |
52 | 已加载修改的 APK!
53 |
54 |
55 | 加载 1.1
56 |
57 |
58 | 已加载 1.1!
59 |
60 |
61 | 创建 Mod 包
62 |
63 |
64 | 已创建 Mod 包!
65 |
66 |
67 | Zip 压缩文件 (*.zip)
68 |
69 |
70 | Android 应用程序包 (*.apk)
71 |
72 |
73 | 无效的保存数据文件夹!请查阅 GameMaker: Studio 1.4 的文档了解有效的保存数据文件夹路径!
74 |
75 |
76 | YoYo Compiler 不支持 Mac!
77 |
78 |
79 | 警告
80 |
81 |
82 | 选择「AM2R_11.zip」
83 |
84 |
85 | 缺失 Mod 名称,作者或版本字段!Mod 打包已中止。
86 |
87 |
88 | 错误
89 |
90 |
91 | 名称中包含无效字符!以下字符不可包含在名称中:
92 |
93 |
94 | AM2R 1.1 的 Zip 压缩文件无效!错误代码:
95 |
96 |
97 | 打包 Mod 中……请稍等片刻!
98 |
99 |
100 | 未检测到修改的 {0} 版游戏,请确保没有将其放置在子文件夹中。\n如此创建的 Mod 包可能无法安装,确定要继续吗?
101 |
102 |
103 | Mac
104 |
105 |
106 | Linux
107 |
108 |
109 | Windows
110 |
111 |
112 | 检测到 profile.xml。AM2RLauncher 使用此文件确认 Mod 的统计信息,包含此文件将可能导致无法安装。确定要继续吗?
113 |
114 |
115 | 保存 {0} 版的 Mod 包
116 |
117 |
118 | Android
119 |
120 |
121 | 请选择修改的 {0} 版 AM2R 的 .{0} 文件
122 |
123 |
124 | apk
125 |
126 |
127 | 已加载修改的 {0} 版游戏!
128 |
129 |
130 | 支持 {0}
131 |
132 |
133 | 加载修改的 {0} 版的 .zip 文件
134 |
135 |
136 | &文件
137 |
138 |
139 | Mod 打包已中止!
140 |
141 |
142 | &退出
143 |
144 |
145 | 自动填充上次使用的字段
146 |
147 |
148 | &偏好设置
149 |
150 |
151 | 偏好设置
152 |
153 |
154 | zip
155 |
156 |
--------------------------------------------------------------------------------
/Atomic/ModPacker.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Eto.Forms;
4 | using Eto.Drawing;
5 | using AtomicLib.XML;
6 | using System.IO;
7 | using System.Net.Mime;
8 | using System.Reflection;
9 | using System.Threading;
10 | using AtomicLib;
11 | using Atomic.Language;
12 | using System.Globalization;
13 | using System.Linq;
14 |
15 | namespace Atomic;
16 |
17 | public partial class ModPacker : Form
18 | {
19 | public ModPacker()
20 | {
21 | // Fill in lookup tables:
22 | #region Lookup Table filling
23 | labelLookupTable = new Dictionary()
24 | {
25 | { ProfileOperatingSystems.Windows, windowsLabel },
26 | { ProfileOperatingSystems.Linux, linuxLabel },
27 | { ProfileOperatingSystems.Mac, macLabel },
28 | { ProfileOperatingSystems.Android, apkLabel },
29 | };
30 | buttonLookupTable = new Dictionary()
31 | {
32 | { ProfileOperatingSystems.Windows, windowsButton },
33 | { ProfileOperatingSystems.Linux, linuxButton },
34 | { ProfileOperatingSystems.Mac, macButton },
35 | { ProfileOperatingSystems.Android, apkButton },
36 | };
37 | checkboxLookupTable = new Dictionary()
38 | {
39 | { ProfileOperatingSystems.Windows, windowsCheckBox },
40 | { ProfileOperatingSystems.Linux, linuxCheckBox },
41 | { ProfileOperatingSystems.Mac, macCheckBox },
42 | { ProfileOperatingSystems.Android, apkCheckBox },
43 | };
44 | modPathLookupTable = new Dictionary()
45 | {
46 | { ProfileOperatingSystems.Windows, modInfo.GetType().GetField(nameof(modInfo.WindowsModPath)) },
47 | { ProfileOperatingSystems.Linux, modInfo.GetType().GetField(nameof(modInfo.LinuxModPath)) },
48 | { ProfileOperatingSystems.Mac, modInfo.GetType().GetField(nameof(modInfo.MacModPath)) },
49 | { ProfileOperatingSystems.Android, modInfo.GetType().GetField(nameof(modInfo.ApkModPath)) },
50 | };
51 | isModLoadedLookupTable = new Dictionary()
52 | {
53 | { ProfileOperatingSystems.Windows, modInfo.GetType().GetProperty(nameof(modInfo.IsWindowsModLoaded)) },
54 | { ProfileOperatingSystems.Linux, modInfo.GetType().GetProperty(nameof(modInfo.IsLinuxModLoaded)) },
55 | { ProfileOperatingSystems.Mac, modInfo.GetType().GetProperty(nameof(modInfo.IsMacModLoaded)) },
56 | { ProfileOperatingSystems.Android, modInfo.GetType().GetProperty(nameof(modInfo.IsApkModLoaded)) },
57 | };
58 | #endregion
59 |
60 | currentConfig = Config.LoadAndReturnConfig();
61 |
62 | if (!currentConfig.Language.Equals("SystemLanguage"))
63 | {
64 | CultureInfo language = CultureInfo.GetCultures(CultureTypes.AllCultures).FirstOrDefault(c => c.NativeName.ToLower().Contains(currentConfig.Language.ToLower()));
65 | if (language is null) currentConfig.Language = "SystemLanguage";
66 | else Thread.CurrentThread.CurrentUICulture = language;
67 | }
68 |
69 | #region Control Text Assignments
70 | nameLabel.Text = Text.ModName;
71 | authorLabel.Text = Text.Author;
72 | versionLabel.Text = Text.Version;
73 | modNotesLabel.Text = Text.ModNotes;
74 |
75 | customSaveCheckBox.Text = Text.UsesCustomSaves;
76 | customSaveButton.Text = Text.SelectFolder;
77 | musicCheckBox.Text = Text.UsesCustomMusic;
78 |
79 | yycCheckBox.Text = Text.UsesYYC;
80 |
81 | windowsCheckBox.Text = String.Format(Text.SupportsOS, Text.Windows);
82 | windowsButton.Text = String.Format(Text.LoadOSZip, Text.Windows);
83 | windowsLabel.Text = String.Format(Text.OSGameLoaded, Text.Windows);
84 |
85 | apkCheckBox.Text = Text.BundleAndroid;
86 | apkButton.Text = Text.LoadAndroidAPK;
87 | apkLabel.Text = Text.AndroidLoaded;
88 |
89 | linuxCheckBox.Text = String.Format(Text.SupportsOS, Text.Linux);
90 | linuxButton.Text = String.Format(Text.LoadOSZip, Text.Linux);
91 | linuxLabel.Text = String.Format(Text.OSGameLoaded, Text.Linux);
92 |
93 | macCheckBox.Text = String.Format(Text.SupportsOS, Text.Mac);
94 | macButton.Text = String.Format(Text.LoadOSZip, Text.Mac);
95 | macLabel.Text = String.Format(Text.OSGameLoaded, Text.Mac);
96 |
97 | originalZipButton.Text = Text.LoadAM2R11;
98 | originalZipLabel.Text = Text.AM2R11Loaded;
99 | createButton.Text = Text.CreateModPackage;
100 | createLabel.Text = Text.ModPackageCreated;
101 | #endregion
102 |
103 | Title = "Atomic v" + Version;
104 | // TODO: "eto bug", this crashes because apparently "compressed symbols aren't supported".
105 | //Icon = new Icon(new MemoryStream(Resources.icon64));
106 | Icon = new Icon(1f, new Bitmap(Resources.icon64));
107 |
108 | MinimumSize = new Size(300, 200);
109 |
110 | var mainContent = new DynamicLayout() { Spacing = new Size(15, 15) };
111 | var leftSide = new DynamicLayout() { Padding = 10, Spacing = new Size(5, 5) };
112 | var rightSide = new DynamicLayout() { Padding = 10, Spacing = new Size(5, 5), };
113 | var rightSideScrollable = new Scrollable() { Border = BorderType.None};
114 |
115 | // Left side
116 | var modInfoPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
117 | modInfoPanel.AddRow(nameLabel, nameTextBox);
118 | modInfoPanel.AddRow(authorLabel, authorTextBox);
119 | modInfoPanel.AddRow(versionLabel, versionTextBox);
120 | modInfoPanel.AddRow(modNotesLabel);
121 |
122 | var modNotesPanel = new DynamicLayout();
123 | modNotesPanel.AddRow(modNotesTextBox);
124 |
125 | // Combine together
126 | leftSide.AddRow(modInfoPanel);
127 | leftSide.AddRow(modNotesPanel);
128 |
129 | // Right Side
130 | var savePanel = new DynamicLayout();
131 | savePanel.AddRow(customSaveCheckBox, null, customSaveButton);
132 |
133 | var saveTextPanel = new DynamicLayout();
134 | saveTextPanel.AddRow(customSaveTextBox);
135 |
136 | var miscOptionsPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
137 | miscOptionsPanel.AddRow(musicCheckBox);
138 | miscOptionsPanel.AddRow(yycCheckBox);
139 | miscOptionsPanel.AddRow(windowsCheckBox);
140 | miscOptionsPanel.AddRow(windowsButton, windowsLabel);
141 | miscOptionsPanel.AddRow(linuxCheckBox);
142 | miscOptionsPanel.AddRow(linuxButton, linuxLabel);
143 | miscOptionsPanel.AddRow(macCheckBox);
144 | miscOptionsPanel.AddRow(macButton, macLabel);
145 | miscOptionsPanel.AddRow(apkCheckBox);
146 | miscOptionsPanel.AddRow(apkButton, apkLabel);
147 |
148 | var loadZipsPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
149 | loadZipsPanel.AddRow(originalZipButton, originalZipLabel);
150 |
151 | var resultPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
152 | resultPanel.AddRow(createButton);
153 | resultPanel.AddRow(createLabel);
154 |
155 | // Combine together
156 | rightSide.AddRow(savePanel);
157 | rightSide.AddRow(saveTextPanel);
158 | rightSide.AddRow(miscOptionsPanel);
159 | rightSide.AddRow(new Label() { Text = "I am a spacer!", TextColor = this.BackgroundColor });
160 | rightSide.AddRow(loadZipsPanel);
161 | rightSide.AddRow(resultPanel);
162 | rightSideScrollable.Content = rightSide;
163 |
164 | // Combine all into main panel and assign
165 | Splitter splitter = new Splitter() {};
166 | splitter.Panel1 = leftSide;
167 | splitter.Panel2 = rightSideScrollable;
168 | splitter.Panel1MinimumSize = 180;
169 | splitter.Panel2MinimumSize = 250;
170 | splitter.Orientation = Orientation.Horizontal;
171 | // HACK: (Client)Size is bugged on GTK before screen is drawn, so I'm having the width hardcoded
172 | splitter.Position = 550 / 2;
173 |
174 | mainContent.Add(splitter);
175 | Content = mainContent;
176 |
177 | // Assign events
178 | originalZipButton.Click += OriginalZipButton_Click;
179 | createButton.Click += CreateButton_Click;
180 | windowsCheckBox.CheckedChanged += (_, _) => OSCheckboxChanged(ProfileOperatingSystems.Windows);
181 | windowsButton.Click += (_, _) => OSButtonClicked(ProfileOperatingSystems.Windows);
182 | apkCheckBox.CheckedChanged += (_, _) => OSCheckboxChanged(ProfileOperatingSystems.Android);
183 | apkButton.Click += (_, _) => OSButtonClicked(ProfileOperatingSystems.Android);
184 | linuxCheckBox.CheckedChanged += (_, _) => OSCheckboxChanged(ProfileOperatingSystems.Linux);
185 | linuxButton.Click += (_, _) => OSButtonClicked(ProfileOperatingSystems.Linux);
186 | macCheckBox.CheckedChanged += MacCheckBox_CheckedChanged;
187 | macButton.Click += (_, _) => OSButtonClicked(ProfileOperatingSystems.Mac);
188 | customSaveCheckBox.CheckedChanged += CustomSaveCheckBoxChecked_Changed;
189 | customSaveButton.Click += CustomSaveDataButton_Click;
190 | yycCheckBox.CheckedChanged += YYCCheckBox_CheckedChanged;
191 | this.Closing += ModPacker_Closing;
192 |
193 | if (currentConfig.FillInContents && currentConfig.Fields != null)
194 | {
195 | nameTextBox.Text = currentConfig.Fields.ModName;
196 | authorTextBox.Text = currentConfig.Fields.Author;
197 | versionTextBox.Text = currentConfig.Fields.Version;
198 | modNotesTextBox.Text = currentConfig.Fields.Notes;
199 | customSaveCheckBox.Checked = currentConfig.Fields.UsesCustomSave;
200 | customSaveTextBox.Text = currentConfig.Fields.CustomSaveDir;
201 | musicCheckBox.Checked = currentConfig.Fields.UsesCustomMusic;
202 | yycCheckBox.Checked = currentConfig.Fields.UsesYYC;
203 | windowsCheckBox.Checked = currentConfig.Fields.SupportsWindows;
204 | linuxCheckBox.Checked = currentConfig.Fields.SupportsLinux;
205 | macCheckBox.Checked = currentConfig.Fields.SupportsMac;
206 | apkCheckBox.Checked = currentConfig.Fields.SupportsAndroid;
207 | }
208 |
209 | // Menu items
210 | var settings = new Command() { MenuText = Text.SettingsMenu, Shortcut = Application.Instance.CommonModifier | Application.Instance.AlternateModifier | Keys.P};
211 | settings.Executed += (sender, args) =>
212 | {
213 | var settings = new SettingsForm(currentConfig);
214 | this.Enabled = false;
215 | settings.ShowModal();
216 | this.Enabled = true;
217 | };
218 | var quit = new Command() { MenuText = Text.QuitMenu, Shortcut = Application.Instance.CommonModifier | Keys.Q};
219 | quit.Executed += (sender, args) => Application.Instance.Quit();
220 |
221 | var file = new SubMenuItem() { Text = Text.FileMenu, Items = { settings, quit } };
222 | Menu = new MenuBar() {Items = { file }};
223 | }
224 |
225 | private Config currentConfig;
226 |
227 | #region Design Elements
228 | private Label nameLabel = new Label();
229 | private TextBox nameTextBox = new TextBox();
230 | private Label authorLabel = new Label();
231 | private TextBox authorTextBox = new TextBox();
232 | private Label versionLabel = new Label();
233 | private TextBox versionTextBox = new TextBox();
234 | private Label modNotesLabel = new Label();
235 | private TextArea modNotesTextBox = new TextArea();
236 |
237 | private CheckBox customSaveCheckBox = new CheckBox();
238 | private Button customSaveButton = new Button() { Enabled = false };
239 | // TODO: remove read only and make it correctly respond to user input
240 | private TextBox customSaveTextBox = new TextBox() { ReadOnly = true };
241 | private CheckBox musicCheckBox = new CheckBox();
242 |
243 | private CheckBox yycCheckBox = new CheckBox();
244 |
245 | private CheckBox windowsCheckBox = new CheckBox();
246 | private Button windowsButton = new Button() { Enabled = false };
247 | private Label windowsLabel = new Label() { Visible = false };
248 |
249 | private CheckBox apkCheckBox = new CheckBox();
250 | private Button apkButton = new Button() { Enabled = false };
251 | private Label apkLabel = new Label() { Visible = false };
252 |
253 | private CheckBox linuxCheckBox = new CheckBox();
254 | private Button linuxButton = new Button() { Enabled = false };
255 | private Label linuxLabel = new Label() { Visible = false };
256 |
257 | private CheckBox macCheckBox = new CheckBox();
258 | private Button macButton = new Button() { Enabled = false };
259 | private Label macLabel = new Label() { Visible = false };
260 |
261 | private Button originalZipButton = new Button();
262 | private Label originalZipLabel = new Label() { Visible = false };
263 | private Button createButton = new Button() { Enabled = false };
264 | private Label createLabel = new Label() { Visible = false };
265 | #endregion
266 | }
--------------------------------------------------------------------------------
/Atomic/ModPacker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.IO.Compression;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text.RegularExpressions;
8 | using Atomic.Language;
9 | using AtomicLib;
10 | using Eto.Forms;
11 |
12 | namespace Atomic;
13 |
14 | public partial class ModPacker : Form
15 | {
16 | private const string Version = Core.Version;
17 | private readonly FileFilter apkFileFilter = new FileFilter(Text.APKFileFilter, ".apk");
18 |
19 | private readonly FileFilter zipFileFilter = new FileFilter(Text.ZipArchivesFileFilter, ".zip");
20 | private readonly Dictionary buttonLookupTable;
21 | private readonly Dictionary checkboxLookupTable;
22 | private readonly Dictionary isModLoadedLookupTable;
23 |
24 | // Lookup dictionaries, filled in Constructor
25 | private readonly Dictionary labelLookupTable;
26 |
27 | private readonly ModCreationInfo modInfo = new ModCreationInfo();
28 | private readonly Dictionary modPathLookupTable;
29 |
30 | #region Eto events
31 |
32 | private void CustomSaveCheckBoxChecked_Changed(object sender, EventArgs e)
33 | {
34 | customSaveButton.Enabled = customSaveCheckBox.Checked.Value;
35 | customSaveTextBox.Enabled = customSaveCheckBox.Checked.Value;
36 | UpdateCreateButton();
37 | }
38 |
39 | private void CustomSaveDataButton_Click(object sender, EventArgs e)
40 | {
41 | string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
42 | bool wasSuccessful = false;
43 |
44 | SelectFolderDialog dialog = new SelectFolderDialog();
45 | Regex saveRegex = null;
46 | string initialDir = "";
47 | if (OS.IsWindows)
48 | {
49 | initialDir = Environment.GetEnvironmentVariable("LocalAppData");
50 | home = home.Replace("\\", "\\\\"); // This is \ -> \\
51 | saveRegex = new Regex($@"{home}\\AppData\\Local\\"); // This is to ensure, that the save directory is valid.
52 | }
53 | else if (OS.IsLinux)
54 | {
55 | initialDir = home + "/.config";
56 | saveRegex = new Regex($@"{home}/\.config/"); // GMS hardcodes save into ~/.config on linux
57 | }
58 | else if (OS.IsMac)
59 | {
60 | initialDir = $@"{home}/Library/Application Support/";
61 | saveRegex = new Regex($@"{home}/Library/Application Support/");
62 | }
63 | else
64 | {
65 | throw new NotSupportedException("The current operating system is not supported!");
66 | }
67 |
68 | dialog.Directory = initialDir;
69 | // TODO: clean this while loop a little
70 | string saveFolderPath = null;
71 | while (!wasSuccessful)
72 | {
73 | if (dialog.ShowDialog(this) == DialogResult.Ok)
74 | {
75 | Match match = saveRegex.Match(dialog.Directory);
76 |
77 | if (match.Success == false)
78 | {
79 | MessageBox.Show(this, Text.InvalidSaveDirectory);
80 | }
81 | else
82 | {
83 | wasSuccessful = true;
84 | saveFolderPath = dialog.Directory.Replace(match.Value, "%localappdata%/");
85 | // If we don't do this, custom save locations are going to fail on Linux
86 | if (OS.IsWindows)
87 | saveFolderPath = saveFolderPath.Replace("\\", "/");
88 |
89 | // On Mac, we need to adjust the path
90 | if (OS.IsMac)
91 | saveFolderPath = saveFolderPath.Replace("com.yoyogames.am2r", "AM2R");
92 |
93 | // if someone has a custom save path inside of am2r and creates these within game maker, they will always be lower case
94 | // we need to adjust them here to lowercase as well, as otherwise launcher gets problems on nix systems
95 | const string vanillaPrefix = "%localappdata%/AM2R/";
96 | if (saveFolderPath.Contains(vanillaPrefix))
97 | saveFolderPath = vanillaPrefix + saveFolderPath.Substring(vanillaPrefix.Length).ToLower();
98 | }
99 | }
100 | else
101 | {
102 | wasSuccessful = true;
103 | }
104 | }
105 |
106 | customSaveTextBox.Text = saveFolderPath;
107 | UpdateCreateButton();
108 | }
109 |
110 | private void YYCCheckBox_CheckedChanged(object sender, EventArgs e)
111 | {
112 | // Don't do anything if it's been disabled
113 | if (!yycCheckBox.Checked.Value)
114 | return;
115 | // Disable mac stuff, as its incompatible with yyc
116 | macCheckBox.Checked = false;
117 | macLabel.Visible = false;
118 | macButton.Enabled = false;
119 | modInfo.MacModPath = null;
120 | }
121 |
122 | private void MacCheckBox_CheckedChanged(object sender, EventArgs e)
123 | {
124 | if (!yycCheckBox.Checked.Value)
125 | {
126 | OSCheckboxChanged(ProfileOperatingSystems.Mac);
127 | }
128 | else if (macCheckBox.Checked.Value)
129 | {
130 | MessageBox.Show(this, Text.YYCMacUnsupported, Text.Warning, MessageBoxButtons.OK, MessageBoxType.Warning);
131 | macCheckBox.Checked = false;
132 | }
133 | }
134 |
135 | private void OriginalZipButton_Click(object sender, EventArgs e)
136 | {
137 | // Open window to select AM2R 1.1
138 | modInfo.AM2R11Path = SelectFile(Text.SelectAM2R11FileDialog, zipFileFilter);
139 | originalZipLabel.Visible = modInfo.IsAM2R11Loaded;
140 | UpdateCreateButton();
141 | }
142 |
143 | private void CreateButton_Click(object sender, EventArgs e)
144 | {
145 | if (String.IsNullOrWhiteSpace(nameTextBox.Text) || String.IsNullOrWhiteSpace(authorTextBox.Text) || String.IsNullOrWhiteSpace(versionTextBox.Text))
146 | {
147 | MessageBox.Show(this, Text.FieldsMissing, Text.Error, MessageBoxButtons.OK, MessageBoxType.Error);
148 | return;
149 | }
150 |
151 | // Trim the mod name textbox so that it has no leading/ending whitespace.
152 | // Also check for invalid file names
153 | nameTextBox.Text = nameTextBox.Text.Trim();
154 |
155 | if (Path.GetInvalidFileNameChars().Any(nameTextBox.Text.Contains))
156 | {
157 | MessageBox.Show(this, Text.NameInvalidCharacters + "\n" + String.Join("\n", Path.GetInvalidFileNameChars()));
158 | return;
159 | }
160 |
161 | // Verify 1.1
162 | IsZipAM2R11ReturnCodes result11 = Core.CheckIfZipIsAM2R11(modInfo.AM2R11Path);
163 | if (result11 != IsZipAM2R11ReturnCodes.Successful)
164 | {
165 | MessageBox.Show(this, Text.AM2R11Invalid + " " + result11);
166 | AbortPatch();
167 | return;
168 | }
169 |
170 | createLabel.Visible = true;
171 | createLabel.Text = Text.PackagingMods;
172 |
173 | bool PromptAndSaveOSMod(ProfileOperatingSystems os)
174 | {
175 | string output;
176 |
177 | using (SaveFileDialog saveFile = new SaveFileDialog { Title = String.Format(Text.SaveOSModProfile, GetLocalizedStringOfOS(os)), Filters = { zipFileFilter } })
178 | {
179 | if (saveFile.ShowDialog(this) == DialogResult.Ok)
180 | {
181 | output = saveFile.FileName;
182 | }
183 | else
184 | {
185 | createLabel.Text = Text.ModPackagingAborted;
186 | return false;
187 | }
188 | }
189 |
190 | // Some filepickers don't automatically set the file extension
191 | if (!output.ToLower().EndsWith(".zip"))
192 | output += ".zip";
193 | LoadProfileParameters(os);
194 | try
195 | {
196 | Core.CreateModPack(modInfo, output);
197 | }
198 | catch (Exception exception)
199 | {
200 | MessageBox.Show(this, exception.ToString(), Text.Error, MessageBoxButtons.OK, MessageBoxType.Error);
201 | AbortPatch();
202 | return false;
203 | }
204 |
205 | return true;
206 | }
207 |
208 | bool CheckForProfileXML(ZipArchive zipFile)
209 | {
210 | if (zipFile.Entries.All(f => f.Name != "profile.xml"))
211 | return true;
212 | DialogResult result = MessageBox.Show(this, Text.ProfileXMLFound, Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
213 | if (result == DialogResult.Yes)
214 | return true;
215 | AbortPatch();
216 | return false;
217 | }
218 |
219 | // TODO: look if its not possible to generalize these
220 | if (windowsCheckBox.Checked.Value)
221 | {
222 | ZipArchive windowsZip = ZipFile.Open(modInfo.WindowsModPath, ZipArchiveMode.Read);
223 | if (windowsZip.Entries.All(f => f.FullName != "AM2R.exe"))
224 | {
225 | // TODO: make method for these $1 replacements
226 | DialogResult result = MessageBox.Show(this, String.Format(Text.ModdedGameNotFound, Text.Windows), Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
227 | if (result != DialogResult.Yes)
228 | {
229 | AbortPatch();
230 | return;
231 | }
232 | }
233 |
234 | if (!CheckForProfileXML(windowsZip))
235 | return;
236 |
237 | if (!PromptAndSaveOSMod(ProfileOperatingSystems.Windows))
238 | return;
239 | }
240 |
241 | if (linuxCheckBox.Checked.Value)
242 | {
243 | ZipArchive linuxZip = ZipFile.Open(modInfo.LinuxModPath, ZipArchiveMode.Read);
244 | if (linuxZip.Entries.All(f => f.FullName != "AM2R") && linuxZip.Entries.All(f => f.FullName != "runner"))
245 | {
246 | DialogResult result = MessageBox.Show(this, String.Format(Text.ModdedGameNotFound, Text.Linux), Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
247 | if (result != DialogResult.Yes)
248 | {
249 | AbortPatch();
250 | return;
251 | }
252 | }
253 |
254 | if (!CheckForProfileXML(linuxZip))
255 | return;
256 |
257 | if (!PromptAndSaveOSMod(ProfileOperatingSystems.Linux))
258 | return;
259 | }
260 |
261 | if (macCheckBox.Checked.Value)
262 | {
263 | ZipArchive macZip = ZipFile.Open(modInfo.MacModPath, ZipArchiveMode.Read);
264 | if (macZip.Entries.All(f => f.FullName != "AM2R.app/Contents/MacOS/Mac_Runner"))
265 | {
266 | DialogResult result = MessageBox.Show(this, String.Format(Text.ModdedGameNotFound, Text.Mac), Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
267 | if (result != DialogResult.Yes)
268 | AbortPatch();
269 | }
270 |
271 | if (!CheckForProfileXML(macZip))
272 | return;
273 |
274 | if (!PromptAndSaveOSMod(ProfileOperatingSystems.Mac))
275 | return;
276 | }
277 |
278 | createLabel.Text = "Mod package(s) created!";
279 | }
280 |
281 | private void ModPacker_Closing(object sender, EventArgs e)
282 | {
283 | if (currentConfig.FillInContents)
284 | {
285 | currentConfig.Fields.ModName = nameTextBox.Text;
286 | currentConfig.Fields.Author = authorTextBox.Text;
287 | currentConfig.Fields.Version = versionTextBox.Text;
288 | currentConfig.Fields.Notes = modNotesTextBox.Text;
289 | currentConfig.Fields.UsesCustomSave = customSaveCheckBox.Checked.Value;
290 | currentConfig.Fields.CustomSaveDir = customSaveTextBox.Text;
291 | currentConfig.Fields.UsesCustomMusic = musicCheckBox.Checked.Value;
292 | currentConfig.Fields.UsesYYC = yycCheckBox.Checked.Value;
293 | currentConfig.Fields.SupportsWindows = windowsCheckBox.Checked.Value;
294 | currentConfig.Fields.SupportsLinux = linuxCheckBox.Checked.Value;
295 | currentConfig.Fields.SupportsMac = macCheckBox.Checked.Value;
296 | currentConfig.Fields.SupportsAndroid = apkCheckBox.Checked.Value;
297 | }
298 |
299 | Config.SaveConfig(currentConfig);
300 | }
301 |
302 | #endregion
303 |
304 | private static string GetLocalizedStringOfOS(ProfileOperatingSystems os)
305 | {
306 | return os switch
307 | {
308 | ProfileOperatingSystems.Windows => Text.Windows,
309 | ProfileOperatingSystems.Linux => Text.Linux,
310 | ProfileOperatingSystems.Mac => Text.Mac,
311 | ProfileOperatingSystems.Android => Text.Android,
312 | _ => "Unknown"
313 | };
314 | }
315 |
316 | private void LoadProfileParameters(ProfileOperatingSystems operatingSystem)
317 | {
318 | // TODO: give the control events where they assign the values directly?
319 | modInfo.Profile.Name = nameTextBox.Text;
320 | modInfo.Profile.Author = authorTextBox.Text;
321 | modInfo.Profile.Version = versionTextBox.Text;
322 | modInfo.Profile.UsesCustomMusic = musicCheckBox.Checked.Value;
323 | modInfo.Profile.UsesYYC = yycCheckBox.Checked.Value;
324 | modInfo.Profile.SupportsAndroid = apkCheckBox.Checked.Value;
325 | modInfo.Profile.ProfileNotes = modNotesTextBox.Text;
326 | modInfo.Profile.OperatingSystem = operatingSystem.ToString();
327 | if (customSaveCheckBox.Checked.Value && !String.IsNullOrWhiteSpace(customSaveTextBox.Text))
328 | modInfo.Profile.SaveLocation = customSaveTextBox.Text;
329 | else
330 | modInfo.Profile.SaveLocation = "%localappdata%/AM2R";
331 | if (operatingSystem == ProfileOperatingSystems.Linux)
332 | {
333 | modInfo.Profile.SaveLocation = modInfo.Profile.SaveLocation.Replace("%localappdata%", "~/.config");
334 | }
335 | else if (operatingSystem == ProfileOperatingSystems.Mac)
336 | {
337 | if (modInfo.Profile.SaveLocation.Contains("%localappdata%/AM2R"))
338 | modInfo.Profile.SaveLocation = modInfo.Profile.SaveLocation.Replace("%localappdata%/AM2R", "~/Library/Application Support/com.yoyogames.am2r");
339 | else
340 | modInfo.Profile.SaveLocation = "~/Library/Application Support/com.yoyogames." + new DirectoryInfo(modInfo.Profile.SaveLocation).Name.ToLower();
341 | }
342 | }
343 |
344 | private void AbortPatch()
345 | {
346 | // Set labels
347 | createLabel.Text = Text.ModPackagingAborted;
348 | }
349 |
350 | private void UpdateCreateButton()
351 | {
352 | if (modInfo.IsAM2R11Loaded && // AM2R_11 zip must be provided
353 | (!windowsCheckBox.Checked.Value || modInfo.IsWindowsModLoaded) && // either Windows is disabled OR windows is provided
354 | (!apkCheckBox.Checked.Value || modInfo.IsApkModLoaded) && // either APK is disabled OR APK is provided
355 | (!linuxCheckBox.Checked.Value || modInfo.IsLinuxModLoaded) && // either Linux is disabled OR linux is provided
356 | (!macCheckBox.Checked.Value || modInfo.IsMacModLoaded) && // either Mac is disabled OR mac is provided
357 | (modInfo.IsWindowsModLoaded || modInfo.IsLinuxModLoaded || modInfo.IsMacModLoaded) && // one desktop OS has to be selected
358 | (!customSaveCheckBox.Checked.Value || customSaveTextBox.Text != "")) // either custom saves are disabled OR custom save is provided
359 | createButton.Enabled = true;
360 | else
361 | createButton.Enabled = false;
362 | }
363 |
364 | private string SelectFile(string title, FileFilter filter)
365 | {
366 | using OpenFileDialog fileFinder = new OpenFileDialog
367 | {
368 | Filters = { filter },
369 | Title = title,
370 | CheckFileExists = true,
371 | };
372 | if (fileFinder.ShowDialog(this) != DialogResult.Ok)
373 | return null;
374 |
375 | string location = fileFinder.FileName;
376 | return location;
377 | }
378 |
379 | private void OSCheckboxChanged(ProfileOperatingSystems os)
380 | {
381 | CheckBox osCheckbox = checkboxLookupTable[os];
382 | Button osButton = buttonLookupTable[os];
383 | Label osLabel = labelLookupTable[os];
384 | FieldInfo osModPath = modPathLookupTable[os];
385 | // If it was disabled, clean the appropriate attributes
386 | osButton.Enabled = osCheckbox.Checked.Value;
387 | if (!osCheckbox.Checked.Value)
388 | {
389 | osLabel.Visible = false;
390 | osModPath.SetValue(modInfo, null);
391 | }
392 |
393 | UpdateCreateButton();
394 | }
395 |
396 | private void OSButtonClicked(ProfileOperatingSystems os)
397 | {
398 | string pickerMessage = String.Format(Text.SelectModdedFile, GetLocalizedStringOfOS(os), os == ProfileOperatingSystems.Android ? Text.APK : Text.Zip);
399 | Label osLabel = labelLookupTable[os];
400 | FieldInfo osModPath = modPathLookupTable[os];
401 | PropertyInfo isOsModLoaded = isModLoadedLookupTable[os];
402 | // Open window to select modded file
403 | string selectedFile = SelectFile(pickerMessage, os == ProfileOperatingSystems.Android ? apkFileFilter : zipFileFilter);
404 | osModPath.SetValue(modInfo, selectedFile);
405 | osLabel.Visible = (bool)isOsModLoaded.GetValue(modInfo);
406 | UpdateCreateButton();
407 | }
408 | }
--------------------------------------------------------------------------------
/Atomic/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 Atomic {
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("Atomic.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 | /// Looks up a localized resource of type System.Byte[].
65 | ///
66 | internal static byte[] icon64 {
67 | get {
68 | object obj = ResourceManager.GetObject("icon64", resourceCulture);
69 | return ((byte[])(obj));
70 | }
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/Atomic/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 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | Resources\icon64.ico;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
123 |
124 |
--------------------------------------------------------------------------------
/Atomic/Resources/icon64.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/Atomic/Resources/icon64.ico
--------------------------------------------------------------------------------
/Atomic/SettingsForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Atomic.Language;
4 | using AtomicLib;
5 | using Eto.Drawing;
6 | using Eto.Forms;
7 |
8 | namespace Atomic;
9 |
10 | public class SettingsForm : Dialog
11 | {
12 | public SettingsForm(Config config)
13 | {
14 | currentConfig = config;
15 | Title = Text.SettingsTitle;
16 | Icon = new Icon(1f, new Bitmap(Resources.icon64));
17 | // TODO: "eto bug" where this dialog behaves very weirdly if you try to resize it. As if the actual size is bigger than the display size
18 | Resizable = false;
19 |
20 | var layout = new DynamicLayout() { Padding = 10, Spacing = new Size(20, 20) };
21 |
22 | List languageList = new List
23 | {
24 | Text.SystemLanguage,
25 | "Deutsch",
26 | "English",
27 | "Español",
28 | "Français",
29 | "Italiano",
30 | "Português",
31 | "Русский",
32 | "日本語",
33 | "中文(简体)"
34 | };
35 |
36 | languageDropDown = new DropDown() { DataStore = languageList };
37 | languageDropDown.SelectedKey = currentConfig.Language == "SystemLanguage" ? Text.SystemLanguage : currentConfig.Language;
38 |
39 | fillInContents = new CheckBox() { Text = Text.RememberFields };
40 | fillInContents.Checked = currentConfig.FillInContents;
41 |
42 | layout.AddRange(Text.LanguageNotice, languageDropDown, fillInContents);
43 |
44 | Content = layout;
45 |
46 | this.Closing += SettingsForm_Closing;
47 | }
48 |
49 | private void SettingsForm_Closing(object sender, EventArgs e)
50 | {
51 | currentConfig.Language = languageDropDown.SelectedKey == Text.SystemLanguage ? "SystemLanguage" : languageDropDown.SelectedKey;
52 | currentConfig.FillInContents = fillInContents.Checked.Value;
53 | Config.SaveConfig(currentConfig);
54 | }
55 |
56 | private Config currentConfig;
57 | private DropDown languageDropDown;
58 | private CheckBox fillInContents;
59 |
60 | }
--------------------------------------------------------------------------------
/AtomicLib/AtomicLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | disable
7 | 10
8 | LatestMajor
9 |
10 |
11 |
12 |
13 | PreserveNewest
14 | true
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/AtomicLib/Config.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Serialization;
2 | using AtomicLib.XML;
3 |
4 | namespace AtomicLib;
5 |
6 | ///
7 | /// Class that handles saving and loading the modpacker configuration.
8 | ///
9 | [Serializable]
10 | [XmlRoot("config")]
11 | public class Config
12 | {
13 | public static readonly string ConfigFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), "Atomic", "config.xml");
14 |
15 | ///
16 | /// Language used for the modpacker.
17 | ///
18 | [XmlAttribute("Language")]
19 | public string Language
20 | { get; set; }
21 |
22 | ///
23 | /// Determines if the modpacker should remember information entered into the fields between usages.
24 | ///
25 | [XmlAttribute("FillInContents")]
26 | public bool FillInContents
27 | { get; set; }
28 |
29 | ///
30 | /// Used to save information from the fields. Only used when is
31 | ///
32 | [XmlElement("Fields")]
33 | public FieldContents Fields
34 | { get; set; }
35 |
36 | public Config()
37 | {
38 |
39 | }
40 |
41 | public Config(string language, bool fillIn)
42 | {
43 | Language = language;
44 | FillInContents = fillIn;
45 | Fields = new FieldContents();
46 | }
47 |
48 | ///
49 | /// Reads the configuration from disk and returns a object. If the configuration does not exist, a default configuration will be created.
50 | ///
51 | /// Returns a object containing either the configuration read from disk or the default one.
52 | public static Config LoadAndReturnConfig()
53 | {
54 | if (!File.Exists(ConfigFilePath))
55 | return CreateAndReturnDefaultConfig();
56 |
57 | return Serializer.Deserialize(File.ReadAllText(ConfigFilePath));
58 | }
59 |
60 | ///
61 | /// Saves a default configuration to disk and returns it, creating the config folders if necessary.
62 | ///
63 | /// Returns the object.
64 | public static Config CreateAndReturnDefaultConfig()
65 | {
66 | Directory.CreateDirectory(Path.GetDirectoryName(ConfigFilePath));
67 | Config defaultConfig = new Config("SystemLanguage", true);
68 | SaveConfig(defaultConfig);
69 | return defaultConfig;
70 | }
71 |
72 | ///
73 | /// Writes the to disk.
74 | ///
75 | /// The that should be saved to the disk.
76 | public static void SaveConfig(Config config)
77 | {
78 | string xmlOutput = Serializer.Serialize(config);
79 | File.WriteAllText(ConfigFilePath, xmlOutput);
80 | }
81 | }
--------------------------------------------------------------------------------
/AtomicLib/Core.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Diagnostics;
3 | using System.IO.Compression;
4 | using System.Security.Cryptography;
5 | using AtomicLib.XML;
6 |
7 | namespace AtomicLib;
8 |
9 | ///
10 | /// An enum, that has all possible operating systems for an AM2R Mod.
11 | ///
12 | public enum ProfileOperatingSystems
13 | {
14 | Unknown,
15 | Windows,
16 | Linux,
17 | Mac,
18 | Android
19 | }
20 |
21 | ///
22 | /// An enum, that has possible return codes for .
23 | ///
24 | public enum IsZipAM2R11ReturnCodes
25 | {
26 | Successful,
27 | MissingOrInvalidAM2RExe,
28 | MissingOrInvalidD3DX943Dll,
29 | MissingOrInvalidDataWin,
30 | GameIsInASubfolder
31 | }
32 |
33 | public static class Core
34 | {
35 | public const string Version = "2.2.0";
36 | private static readonly string localPath = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
37 |
38 | ///
39 | /// Creates an AM2R modpack using information from .
40 | /// Final modpack zip is outputted to .
41 | ///
42 | /// A object containing information about the mod that will be packed.
43 | /// Path where the mod will be outputted to.
44 | public static void CreateModPack(ModCreationInfo modInfo, string output)
45 | {
46 | if (modInfo is null)
47 | throw new NullReferenceException(nameof(modInfo));
48 | if (modInfo.Profile is null)
49 | throw new NullReferenceException(nameof(modInfo.Profile));
50 | if (!File.Exists(modInfo.AM2R11Path))
51 | throw new FileNotFoundException("AM2R_11 file path could not be found!");
52 | if (modInfo.Profile.SupportsAndroid && !File.Exists(modInfo.ApkModPath))
53 | throw new FileNotFoundException("Android is marked as supported, but the APK path (" + modInfo.ApkModPath + ") could not be found!");
54 |
55 | ProfileOperatingSystems profileOS;
56 | if (!Enum.TryParse(modInfo.Profile.OperatingSystem, out profileOS))
57 | profileOS = ProfileOperatingSystems.Unknown;
58 | string modZipPath = profileOS switch
59 | {
60 | ProfileOperatingSystems.Windows => modInfo.WindowsModPath,
61 | ProfileOperatingSystems.Linux => modInfo.LinuxModPath,
62 | ProfileOperatingSystems.Mac => modInfo.MacModPath,
63 | _ => throw new NotSupportedException("The current operating system is not supported!")
64 | };
65 |
66 | if (!File.Exists(modZipPath))
67 | throw new FileNotFoundException("The file path (" + modZipPath + ") for the OS (" + modInfo.Profile.OperatingSystem + ") could not be found!");
68 |
69 | // Cleanup in case of previous errors
70 | if (Directory.Exists($"{Path.GetTempPath()}/Atomic"))
71 | Directory.Delete($"{Path.GetTempPath()}/Atomic", true);
72 |
73 | // Create temp work folders
74 | string tempPath = Directory.CreateDirectory($"{Path.GetTempPath()}/Atomic").FullName;
75 | string tempOriginalPath = Directory.CreateDirectory($"{tempPath}/original").FullName;
76 | string tempModPath = Directory.CreateDirectory($"{tempPath}/mod").FullName;
77 | string tempProfilePath = Directory.CreateDirectory($"{tempPath}/profile").FullName;
78 |
79 | // Extract 1.1 and modded AM2R to their own directories in temp work
80 | // We *probably* should check for 1.1 validity before extracting, *HOWEVER* that makes it kinda difficult to test against.
81 | ZipFile.ExtractToDirectory(modInfo.AM2R11Path, tempOriginalPath);
82 | ZipFile.ExtractToDirectory(modZipPath, tempModPath);
83 |
84 | // There once was a workaround here to work with Linux mods built with GMS1.4, however since then, GMS broke even more and is now seemingly unable to built for Linux.
85 |
86 | // Create AM2R.exe and data.win patches
87 | switch (profileOS)
88 | {
89 | case ProfileOperatingSystems.Windows:
90 | if (modInfo.Profile.UsesYYC)
91 | {
92 | CreatePatch($"{tempOriginalPath}/data.win", $"{tempModPath}/AM2R.exe", $"{tempProfilePath}/AM2R.xdelta");
93 | }
94 | else
95 | {
96 | CreatePatch($"{tempOriginalPath}/data.win", $"{tempModPath}/data.win", $"{tempProfilePath}/data.xdelta");
97 | CreatePatch($"{tempOriginalPath}/AM2R.exe", $"{tempModPath}/AM2R.exe", $"{tempProfilePath}/AM2R.xdelta");
98 | }
99 | break;
100 |
101 | case ProfileOperatingSystems.Linux:
102 | string runnerName = File.Exists($"{tempModPath}/AM2R") ? "AM2R" : "runner";
103 | CreatePatch($"{tempOriginalPath}/data.win", $"{tempModPath}/assets/game.unx", $"{tempProfilePath}/game.xdelta");
104 | CreatePatch($"{tempOriginalPath}/AM2R.exe", $"{tempModPath}/{runnerName}", $"{tempProfilePath}/AM2R.xdelta");
105 | break;
106 |
107 | case ProfileOperatingSystems.Mac:
108 | CreatePatch($"{tempOriginalPath}/data.win", $"{tempModPath}/AM2R.app/Contents/Resources/game.ios", $"{tempProfilePath}/game.xdelta");
109 | CreatePatch($"{tempOriginalPath}/AM2R.exe", $"{tempModPath}/AM2R.app/Contents/MacOS/Mac_Runner", $"{tempProfilePath}/AM2R.xdelta");
110 |
111 | // Copy plist over for custom title name
112 | File.Copy($"{tempModPath}/AM2R.app/Contents/Info.plist", $"{tempProfilePath}/Info.plist");
113 | break;
114 | }
115 |
116 | // Create game.droid patch and wrapper if Android is supported
117 | if (modInfo.Profile.SupportsAndroid)
118 | {
119 | string tempAndroid = Directory.CreateDirectory($"{tempPath}/android").FullName;
120 |
121 | // Extract APK first in order to create patch from the data.win
122 | // java -jar apktool.jar d "AM2RWrapper_old.apk"
123 | RunJavaJar($"\"{localPath}/utilities/android/apktool.jar\" d -f -o \"{tempAndroid}\" \"{modInfo.ApkModPath}\"");
124 |
125 | // Create game.droid patch
126 | CreatePatch($"{tempOriginalPath}/data.win", $"{tempAndroid}/assets/game.droid", $"{tempProfilePath}/droid.xdelta");
127 |
128 | // Delete excess files in APK, so we can use it as a bare-minimum wrapper
129 | // Create whitelist
130 | string[] whitelist = { "splash.png", "portrait_splash.png" };
131 | // Get directory
132 | var androidAssets = new DirectoryInfo($"{tempAndroid}/assets");
133 | // Delete files and folders
134 | foreach (var file in androidAssets.GetFiles())
135 | {
136 | // Not really sure why it's checked like this, but AM2R.ini is a file necessary to boot for YYC
137 | if (file.Name.EndsWith(".ini") && file.Name != "modifiers.ini")
138 | File.Copy(file.FullName, $"{tempProfilePath}/AM2R.ini", true);
139 |
140 | if (!whitelist.Contains(file.Name))
141 | File.Delete(file.FullName);
142 | }
143 | foreach (var dir in androidAssets.GetDirectories())
144 | Directory.Delete(dir.FullName, true);
145 |
146 | // And now we create the wrapper from it
147 | // Process startInfo
148 | // java -jar apktool.jar b "AM2RWrapper_old" -o "AM2RWrapper.apk"
149 | RunJavaJar($"\"{localPath}/utilities/android/apktool.jar\" b -f \"{tempAndroid}\" -o \"{tempProfilePath}/AM2RWrapper.apk\"");
150 |
151 | string tempAndroidWrapperPath = $"{tempProfilePath}/android";
152 | Directory.CreateDirectory(tempAndroidWrapperPath);
153 |
154 | File.Move($"{tempProfilePath}/AM2RWrapper.apk", $"{tempAndroidWrapperPath}/AM2RWrapper.apk");
155 | if (File.Exists($"{tempProfilePath}/AM2R.ini"))
156 | File.Move($"{tempProfilePath}/AM2R.ini", $"{tempAndroidWrapperPath}/AM2R.ini");
157 | }
158 |
159 | // Copy datafiles and exclude .ogg if custom music is not selected
160 | var gameAssetDir = new DirectoryInfo(tempModPath);
161 | if (profileOS == ProfileOperatingSystems.Linux)
162 | gameAssetDir = new DirectoryInfo($"{tempModPath}/assets");
163 | else if (profileOS == ProfileOperatingSystems.Mac)
164 | gameAssetDir = new DirectoryInfo($"{tempModPath}/AM2R.app/Contents/Resources");
165 |
166 | Directory.CreateDirectory($"{tempProfilePath}/files_to_copy");
167 | string[] datafilesBlacklist = { "data.win", "AM2R.exe", "D3DX9_43.dll", "game.unx", "game.ios" };
168 |
169 | if (modInfo.Profile.UsesCustomMusic)
170 | {
171 | // Copy all files, excluding the blacklist
172 | CopyFilesRecursive(gameAssetDir, datafilesBlacklist, $"{tempProfilePath}/files_to_copy");
173 | }
174 | else
175 | {
176 | // Get list of 1.1's music files
177 | string[] musFiles = Directory.GetFiles(tempOriginalPath, "*.ogg").Select(file => Path.GetFileName(file)).ToArray();
178 | // Since on Unix our songs are in lowercase and we want to compare them later, we need to adjust for it here
179 | if (profileOS == ProfileOperatingSystems.Linux || profileOS == ProfileOperatingSystems.Mac)
180 | musFiles = musFiles.Select(f => f.ToLower()).ToArray();
181 | // Combine musFiles with the known datafiles for a blacklist
182 | string[] blacklist = musFiles.Concat(datafilesBlacklist).ToArray();
183 | // Copy files, excluding the blacklist
184 | CopyFilesRecursive(gameAssetDir, blacklist, $"{tempProfilePath}/files_to_copy");
185 | }
186 |
187 | // Export profile as XML
188 | string xmlOutput = Serializer.Serialize(modInfo.Profile);
189 | File.WriteAllText($"{tempProfilePath}/profile.xml", xmlOutput);
190 |
191 | // Compress temp folder to .zip
192 | if (File.Exists(output))
193 | File.Delete(output);
194 |
195 | ZipFile.CreateFromDirectory(tempProfilePath, output);
196 |
197 | // Delete temp folder
198 | Directory.Delete(tempPath, true);
199 | }
200 |
201 | ///
202 | /// Uses xdelta3 to create a patch file that represents the differences
203 | /// between and .
204 | /// Outputs to .
205 | ///
206 | /// Path to the original file.
207 | /// Path to the modified file.
208 | /// Path where the patch will be outputted to.
209 | public static void CreatePatch(string original, string modified, string output)
210 | {
211 | // Specify process start info
212 | var parameters = new ProcessStartInfo
213 | {
214 | FileName = OS.IsWindows ? localPath + "/utilities/xdelta/xdelta3.exe" : "xdelta3",
215 | WorkingDirectory = localPath,
216 | UseShellExecute = false,
217 | CreateNoWindow = true,
218 | Arguments = "-f -e -s \"" + original + "\" \"" + modified + "\" \"" + output + "\""
219 | };
220 |
221 | // Launch process and wait for exit.
222 | try
223 | {
224 | using var proc = new Process { StartInfo = parameters };
225 | proc.Start();
226 | proc.WaitForExit();
227 | }
228 | catch (Win32Exception)
229 | {
230 | throw new IOException("Xdelta3 could not be found! For Windows, make sure that the utilities folder exists, for other OS make sure it is installed and in PATH.");
231 | }
232 | }
233 |
234 | ///
235 | /// Executes a given Java jar file and passes the specified
236 | /// to it.
237 | /// Optionally accepts a .
238 | ///
239 | /// Path to the Java jar file and other arguments to be used when running it.
240 | /// Path to a directory to use as the Working Directory when executing the Java jar file.
241 | /// Will use the current directory if not specified.
242 | public static void RunJavaJar(string arguments = null, string workingDirectory = null)
243 | {
244 | workingDirectory ??= Directory.GetCurrentDirectory();
245 | string proc = "",
246 | javaArgs = "";
247 |
248 | if (OS.IsWindows)
249 | {
250 | proc = "cmd";
251 | javaArgs = "/C java -jar";
252 | }
253 | else if (OS.IsUnix)
254 | {
255 | proc = "java";
256 | javaArgs = "-jar";
257 | }
258 |
259 | ProcessStartInfo jarStart = new ProcessStartInfo
260 | {
261 | FileName = proc,
262 | Arguments = $"{javaArgs} {arguments}",
263 | WorkingDirectory = workingDirectory,
264 | UseShellExecute = false,
265 | CreateNoWindow = true
266 | };
267 |
268 | Process jarProcess = new Process
269 | {
270 | StartInfo = jarStart
271 | };
272 |
273 | try
274 | {
275 | jarProcess.Start();
276 | jarProcess.WaitForExit();
277 | }
278 | catch
279 | {
280 | throw new IOException("Java could not be found! Make sure it is installed and in PATH.");
281 | }
282 |
283 | }
284 |
285 | // Taken from AM2RLauncher
286 | ///
287 | /// Checks if a Zip file is a valid AM2R_1.1 zip.
288 | ///
289 | /// Full Path to the Zip file to validate.
290 | /// detailing the result
291 | public static IsZipAM2R11ReturnCodes CheckIfZipIsAM2R11(string zipPath)
292 | {
293 | const string d3dHash = "86e39e9161c3d930d93822f1563c280d";
294 | const string dataWinHash = "f2b84fe5ba64cb64e284be1066ca08ee";
295 | const string am2rHash = "15253f7a66d6ea3feef004ebbee9b438";
296 | string tmpPath = Path.GetTempPath() + "/" + Path.GetFileNameWithoutExtension(zipPath);
297 |
298 | // Clean up in case folder exists already
299 | if (Directory.Exists(tmpPath))
300 | Directory.Delete(tmpPath, true);
301 | Directory.CreateDirectory(tmpPath);
302 |
303 | // Open archive
304 | ZipArchive am2rZip = ZipFile.OpenRead(zipPath);
305 |
306 | // Check if exe exists anywhere
307 | ZipArchiveEntry am2rExe = am2rZip.Entries.FirstOrDefault(x => x.FullName.Contains("AM2R.exe"));
308 | if (am2rExe == null)
309 | return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
310 |
311 | // Check if it's not in a subfolder. if it'd be in a subfolder, fullname would be "folder/AM2R.exe"
312 | if (am2rExe.FullName != "AM2R.exe")
313 | return IsZipAM2R11ReturnCodes.GameIsInASubfolder;
314 |
315 | // Check validity
316 | am2rExe.ExtractToFile($"{tmpPath}/{am2rExe.FullName}");
317 | if (CalculateMD5($"{tmpPath}/{am2rExe.FullName}") != am2rHash)
318 | return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
319 |
320 |
321 | // Check if data.win exists / is valid
322 | ZipArchiveEntry dataWin = am2rZip.Entries.FirstOrDefault(x => x.FullName == "data.win");
323 | if (dataWin == null)
324 | return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
325 |
326 | dataWin.ExtractToFile($"{tmpPath}/{dataWin.FullName}");
327 | if (CalculateMD5($"{tmpPath}/{dataWin.FullName}") != dataWinHash)
328 | return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
329 |
330 |
331 | // Check if d3d.dll exists / is valid
332 | ZipArchiveEntry d3dx = am2rZip.Entries.FirstOrDefault(x => x.FullName == "D3DX9_43.dll");
333 | if (d3dx == null)
334 | return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX943Dll;
335 |
336 | d3dx.ExtractToFile($"{tmpPath}/{d3dx.FullName}");
337 | if (CalculateMD5($"{tmpPath}/{d3dx.FullName}") != d3dHash)
338 | return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX943Dll;
339 |
340 | // Clean up
341 | Directory.Delete(tmpPath, true);
342 |
343 | // If we didn't exit before, everything is fine
344 | return IsZipAM2R11ReturnCodes.Successful;
345 | }
346 |
347 | private static string CalculateMD5(string filename)
348 | {
349 | using var stream = File.OpenRead(filename);
350 | using var md5 = MD5.Create();
351 | byte[] hash = md5.ComputeHash(stream);
352 | return BitConverter.ToString(hash).Replace("-", "").ToLower();
353 | }
354 |
355 | private static void CopyFilesRecursive(DirectoryInfo source, string[] blacklist, string destination)
356 | {
357 | foreach (var file in source.GetFiles())
358 | {
359 | if (!blacklist.Contains(file.Name, StringComparer.OrdinalIgnoreCase))
360 | file.CopyTo(destination + "/" + file.Name);
361 | }
362 |
363 | foreach (var dir in source.GetDirectories())
364 | {
365 | // Folders need to be lowercase, because GM only reads from lowercase names on *nix systems. Windows is case-insensitive so doesnt matter for them
366 | string newDir = Directory.CreateDirectory(destination + "/" + dir.Name.ToLower()).FullName;
367 | CopyFilesRecursive(dir, blacklist, newDir);
368 | }
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/AtomicLib/FieldContents.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Serialization;
2 |
3 | namespace AtomicLib;
4 |
5 | ///
6 | /// Class that handles how field information is saved as XML.
7 | ///
8 | [Serializable]
9 | [XmlRoot("fieldcontents")]
10 |
11 | public class FieldContents
12 | {
13 | /// The text entered into a field for the mod name.
14 | [XmlAttribute("ModName")]
15 | public string ModName
16 | { get; set; }
17 | /// The text entered into a field for the mod author.
18 | [XmlAttribute("Author")]
19 | public string Author
20 | { get; set; }
21 | /// The text entered into a field for the mod version.
22 | [XmlAttribute("Version")]
23 | public string Version
24 | { get; set; }
25 | /// The text entered into a field for the mod notes.
26 | [XmlAttribute("Notes")]
27 | public string Notes
28 | { get; set; }
29 | /// The checkbox that indicates if the mod uses custom saves.
30 | [XmlAttribute("UsesCustomSave")]
31 | public bool UsesCustomSave
32 | { get; set; }
33 | /// The text that indicates the path used to store the mod's custom saves.
34 | [XmlAttribute("CustomSaveDir")]
35 | public string CustomSaveDir
36 | { get; set; }
37 | /// The checkbox that indicates if the mod uses custom music.
38 | [XmlAttribute("UsesCustomMusic")]
39 | public bool UsesCustomMusic
40 | { get; set; }
41 | /// The checkbox that indicates if the mod uses the YoYo Compiler.
42 | [XmlAttribute("UsesYYC")]
43 | public bool UsesYYC
44 | { get; set; }
45 | /// The checkbox that indicates if the mod supports Windows
46 | [XmlAttribute("SupportsWindows")]
47 | public bool SupportsWindows
48 | { get; set; }
49 | /// The checkbox that indicates if the mod supports Linux
50 | [XmlAttribute("SupportsLinux")]
51 | public bool SupportsLinux
52 | { get; set; }
53 | /// The checkbox that indicates if the mod supports Mac
54 | [XmlAttribute("SupportsMac")]
55 | public bool SupportsMac
56 | { get; set; }
57 | /// The checkbox that indicates if the mod supports Android
58 | [XmlAttribute("SupportsAndroid")]
59 | public bool SupportsAndroid
60 | { get; set; }
61 |
62 | public FieldContents()
63 | {
64 |
65 | }
66 | }
--------------------------------------------------------------------------------
/AtomicLib/ModCreationInfo.cs:
--------------------------------------------------------------------------------
1 | using AtomicLib.XML;
2 |
3 | namespace AtomicLib;
4 |
5 | // TODO: documentt his class
6 | public class ModCreationInfo
7 | {
8 | public ModProfileXML Profile = new ModProfileXML();
9 |
10 | public bool IsAM2R11Loaded => !String.IsNullOrWhiteSpace(AM2R11Path);
11 | public bool IsWindowsModLoaded => !String.IsNullOrWhiteSpace(WindowsModPath);
12 | public bool IsApkModLoaded => !String.IsNullOrWhiteSpace(ApkModPath);
13 | public bool IsLinuxModLoaded=> !String.IsNullOrWhiteSpace(LinuxModPath);
14 | public bool IsMacModLoaded => !String.IsNullOrWhiteSpace(MacModPath);
15 |
16 | public string AM2R11Path;
17 | public string WindowsModPath;
18 | public string ApkModPath;
19 | public string LinuxModPath;
20 | public string MacModPath;
21 | }
--------------------------------------------------------------------------------
/AtomicLib/OS.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace AtomicLib;
4 |
5 | ///
6 | /// Class that has information about the current running operating system.
7 | ///
8 | public static class OS
9 | {
10 | ///
11 | /// Determines if the current OS is Windows.
12 | ///
13 | public static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
14 |
15 | ///
16 | /// Determines if the current OS is Linux.
17 | ///
18 | public static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
19 |
20 | ///
21 | /// Determines if the current OS is Mac.
22 | ///
23 | public static readonly bool IsMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
24 |
25 | ///
26 | /// Determines if the current OS is a unix based system (Mac or Linux).
27 | ///
28 | public static readonly bool IsUnix = IsLinux || IsMac;
29 |
30 | ///
31 | /// Gets a string representation of the current OS.
32 | ///
33 | public static readonly string Name = DetermineOsName();
34 |
35 | ///
36 | /// Generates a string representation of the current OS
37 | ///
38 | private static string DetermineOsName()
39 | {
40 | if (IsWindows)
41 | return "Windows";
42 | if (IsLinux)
43 | return "Linux";
44 | if (IsMac)
45 | return "Mac";
46 |
47 | return "Unknown OS";
48 | }
49 |
50 | ///
51 | /// Checks if this is run via WINE.
52 | ///
53 | public static readonly bool IsThisRunningFromWINE = CheckIfRunFromWINE();
54 |
55 | ///
56 | /// Checks if this is run via Flatpak.
57 | ///
58 | public static readonly bool IsThisRunningFromFlatpak = CheckIfRunFromFlatpak();
59 |
60 | ///
61 | /// Checks if the Launcher is ran from WINE.
62 | ///
63 | /// if run from WINE, if not.
64 | private static bool CheckIfRunFromWINE()
65 | {
66 | // We check for wine by seeing if a reg entry exists.
67 | // Not the best way, and could be removed from the future, but good enough for our purposes.
68 | #pragma warning disable CA1416
69 | if (IsWindows && (Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Wine") != null))
70 | return true;
71 | #pragma warning restore CA1416
72 |
73 | return false;
74 | }
75 |
76 | ///
77 | /// Checks if the Launcher is ran from a Flatpak.
78 | ///
79 | /// if run from a Flatpak, if not.
80 | private static bool CheckIfRunFromFlatpak()
81 | {
82 | if (!IsLinux) return false;
83 |
84 | // This file is present in all flatpaks
85 | if (File.Exists("/.flatpak-info"))
86 | return true;
87 | return false;
88 | }
89 | }
--------------------------------------------------------------------------------
/AtomicLib/XML/ModProfileXML.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Serialization;
2 |
3 | namespace AtomicLib.XML;
4 |
5 | ///
6 | /// Class that handles how the mod settings are saved as XML.
7 | ///
8 | [Serializable]
9 | [XmlRoot("message")]
10 | public class ModProfileXML
11 | {
12 | /// Indicates the Operating system the mod was made for.
13 | [XmlAttribute("OperatingSystem")]
14 | public string OperatingSystem
15 | { get; set; }
16 | /// Indicates the xml version the mod was made in.
17 | [XmlAttribute("XMLVersion")]
18 | public int XMLVersion
19 | { get; set; }
20 | /// Indicates the version of the mod.
21 | [XmlAttribute("Version")]
22 | public string Version
23 | { get; set; }
24 | /// Indicates the mod's name.
25 | [XmlAttribute("Name")]
26 | public string Name
27 | { get; set; }
28 | /// Indicates the mod's author.
29 | [XmlAttribute("Author")]
30 | public string Author
31 | { get; set; }
32 | /// Indicates whether or not the mod uses custom music.
33 | [XmlAttribute("UsesCustomMusic")]
34 | public bool UsesCustomMusic
35 | { get; set; }
36 | /// Indicates the save location of the mod.
37 | [XmlAttribute("SaveLocation")]
38 | public string SaveLocation
39 | { get; set; }
40 | /// Indicates whether or not the mod supports Android.
41 | [XmlAttribute("SupportsAndroid")]
42 | public bool SupportsAndroid
43 | { get; set; }
44 | /// Indicates whether or not the mod was compiled with YYC.
45 | [XmlAttribute("UsesYYC")]
46 | public bool UsesYYC
47 | { get; set; }
48 | /// Indicates if the mod is installable. This is only for archival community updates mods.
49 | [XmlAttribute("Installable")]
50 | public bool Installable
51 | { get; set; }
52 | /// Indicates any notes that the mod author deemed worthy to share about his mod.
53 | [XmlAttribute("ProfileNotes")]
54 | public string ProfileNotes
55 | { get; set; }
56 |
57 | /// Creates a with a default set of attributes.
58 | public ModProfileXML()
59 | {
60 | XMLVersion = 1;
61 | Installable = true;
62 | }
63 |
64 | ///
65 | /// Creates a with a custom set of attributes.
66 | ///
67 | /// The operating system the mod was made on.
68 | /// The xml version the mod was created with.
69 | /// The version of the mod.
70 | /// The mod name.
71 | /// The mod author.
72 | /// Whether or not the mod uses custom music.
73 | /// The save location of the mod.
74 | /// Whether or not the mod works for android.
75 | /// Whether or not the mod was made with YYC.
76 | /// Whether or not the mod is installable.
77 | /// The notes of the mod.
78 | public ModProfileXML(string operatingSystem, int xmlVersion, string version, string name, string author,
79 | bool usesCustomMusic, string saveLocation, bool android, bool usesYYC,
80 | bool installable, string profileNotes)
81 | {
82 | OperatingSystem = operatingSystem;
83 | XMLVersion = xmlVersion;
84 | Version = version;
85 | Name = name;
86 | Author = author;
87 | UsesCustomMusic = usesCustomMusic;
88 | SaveLocation = saveLocation;
89 | SupportsAndroid = android;
90 | UsesYYC = usesYYC;
91 | Installable = installable;
92 | ProfileNotes = profileNotes;
93 | }
94 | }
--------------------------------------------------------------------------------
/AtomicLib/XML/Serializer.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace AtomicLib.XML;
4 |
5 | ///
6 | /// The Serializer class, that serializes to and deserializes from XML files.
7 | ///
8 | public class Serializer
9 | {
10 | ///
11 | /// Serializes as a to XML.
12 | ///
13 | /// The class to serialize to.
14 | /// The object that will be serialized.
15 | /// The serialized XML as a .
16 | public static string Serialize(object item)
17 | {
18 | Type t = typeof(T);
19 | MemoryStream memStream = new MemoryStream();
20 | System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t);
21 |
22 | serializer.Serialize(memStream, item);
23 |
24 | string xml = Encoding.UTF8.GetString(memStream.ToArray());
25 |
26 | memStream.Flush();
27 | memStream.Close();
28 | memStream.Dispose();
29 | memStream = null;
30 |
31 | return xml;
32 | }
33 |
34 | ///
35 | /// Deserialize into an object of class that can be assigned.
36 | ///
37 | /// The class that will be deserialized to.
38 | /// An XML that will be deserialized.
39 | /// A deserialized object of class from .
40 | public static T Deserialize(string xmlString)
41 | {
42 | Type t = typeof(T);
43 | using (MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString)))
44 | {
45 | System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t);
46 | return (T)serializer.Deserialize(memStream);
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/AtomicLib/utilities/android/LICENSE:
--------------------------------------------------------------------------------
1 | Sub projects brut.apktool, brut.j.common, brut.dir and brut.j.util are
2 | released under the following license:
3 |
4 | *******************************************************************************
5 |
6 | Apache License
7 | Version 2.0, January 2004
8 | http://www.apache.org/licenses/
9 |
10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
11 |
12 | 1. Definitions.
13 |
14 | "License" shall mean the terms and conditions for use, reproduction,
15 | and distribution as defined by Sections 1 through 9 of this document.
16 |
17 | "Licensor" shall mean the copyright owner or entity authorized by
18 | the copyright owner that is granting the License.
19 |
20 | "Legal Entity" shall mean the union of the acting entity and all
21 | other entities that control, are controlled by, or are under common
22 | control with that entity. For the purposes of this definition,
23 | "control" means (i) the power, direct or indirect, to cause the
24 | direction or management of such entity, whether by contract or
25 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
26 | outstanding shares, or (iii) beneficial ownership of such entity.
27 |
28 | "You" (or "Your") shall mean an individual or Legal Entity
29 | exercising permissions granted by this License.
30 |
31 | "Source" form shall mean the preferred form for making modifications,
32 | including but not limited to software source code, documentation
33 | source, and configuration files.
34 |
35 | "Object" form shall mean any form resulting from mechanical
36 | transformation or translation of a Source form, including but
37 | not limited to compiled object code, generated documentation,
38 | and conversions to other media types.
39 |
40 | "Work" shall mean the work of authorship, whether in Source or
41 | Object form, made available under the License, as indicated by a
42 | copyright notice that is included in or attached to the work
43 | (an example is provided in the Appendix below).
44 |
45 | "Derivative Works" shall mean any work, whether in Source or Object
46 | form, that is based on (or derived from) the Work and for which the
47 | editorial revisions, annotations, elaborations, or other modifications
48 | represent, as a whole, an original work of authorship. For the purposes
49 | of this License, Derivative Works shall not include works that remain
50 | separable from, or merely link (or bind by name) to the interfaces of,
51 | the Work and Derivative Works thereof.
52 |
53 | "Contribution" shall mean any work of authorship, including
54 | the original version of the Work and any modifications or additions
55 | to that Work or Derivative Works thereof, that is intentionally
56 | submitted to Licensor for inclusion in the Work by the copyright owner
57 | or by an individual or Legal Entity authorized to submit on behalf of
58 | the copyright owner. For the purposes of this definition, "submitted"
59 | means any form of electronic, verbal, or written communication sent
60 | to the Licensor or its representatives, including but not limited to
61 | communication on electronic mailing lists, source code control systems,
62 | and issue tracking systems that are managed by, or on behalf of, the
63 | Licensor for the purpose of discussing and improving the Work, but
64 | excluding communication that is conspicuously marked or otherwise
65 | designated in writing by the copyright owner as "Not a Contribution."
66 |
67 | "Contributor" shall mean Licensor and any individual or Legal Entity
68 | on behalf of whom a Contribution has been received by Licensor and
69 | subsequently incorporated within the Work.
70 |
71 | 2. Grant of Copyright License. Subject to the terms and conditions of
72 | this License, each Contributor hereby grants to You a perpetual,
73 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
74 | copyright license to reproduce, prepare Derivative Works of,
75 | publicly display, publicly perform, sublicense, and distribute the
76 | Work and such Derivative Works in Source or Object form.
77 |
78 | 3. Grant of Patent License. Subject to the terms and conditions of
79 | this License, each Contributor hereby grants to You a perpetual,
80 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
81 | (except as stated in this section) patent license to make, have made,
82 | use, offer to sell, sell, import, and otherwise transfer the Work,
83 | where such license applies only to those patent claims licensable
84 | by such Contributor that are necessarily infringed by their
85 | Contribution(s) alone or by combination of their Contribution(s)
86 | with the Work to which such Contribution(s) was submitted. If You
87 | institute patent litigation against any entity (including a
88 | cross-claim or counterclaim in a lawsuit) alleging that the Work
89 | or a Contribution incorporated within the Work constitutes direct
90 | or contributory patent infringement, then any patent licenses
91 | granted to You under this License for that Work shall terminate
92 | as of the date such litigation is filed.
93 |
94 | 4. Redistribution. You may reproduce and distribute copies of the
95 | Work or Derivative Works thereof in any medium, with or without
96 | modifications, and in Source or Object form, provided that You
97 | meet the following conditions:
98 |
99 | (a) You must give any other recipients of the Work or
100 | Derivative Works a copy of this License; and
101 |
102 | (b) You must cause any modified files to carry prominent notices
103 | stating that You changed the files; and
104 |
105 | (c) You must retain, in the Source form of any Derivative Works
106 | that You distribute, all copyright, patent, trademark, and
107 | attribution notices from the Source form of the Work,
108 | excluding those notices that do not pertain to any part of
109 | the Derivative Works; and
110 |
111 | (d) If the Work includes a "NOTICE" text file as part of its
112 | distribution, then any Derivative Works that You distribute must
113 | include a readable copy of the attribution notices contained
114 | within such NOTICE file, excluding those notices that do not
115 | pertain to any part of the Derivative Works, in at least one
116 | of the following places: within a NOTICE text file distributed
117 | as part of the Derivative Works; within the Source form or
118 | documentation, if provided along with the Derivative Works; or,
119 | within a display generated by the Derivative Works, if and
120 | wherever such third-party notices normally appear. The contents
121 | of the NOTICE file are for informational purposes only and
122 | do not modify the License. You may add Your own attribution
123 | notices within Derivative Works that You distribute, alongside
124 | or as an addendum to the NOTICE text from the Work, provided
125 | that such additional attribution notices cannot be construed
126 | as modifying the License.
127 |
128 | You may add Your own copyright statement to Your modifications and
129 | may provide additional or different license terms and conditions
130 | for use, reproduction, or distribution of Your modifications, or
131 | for any such Derivative Works as a whole, provided Your use,
132 | reproduction, and distribution of the Work otherwise complies with
133 | the conditions stated in this License.
134 |
135 | 5. Submission of Contributions. Unless You explicitly state otherwise,
136 | any Contribution intentionally submitted for inclusion in the Work
137 | by You to the Licensor shall be under the terms and conditions of
138 | this License, without any additional terms or conditions.
139 | Notwithstanding the above, nothing herein shall supersede or modify
140 | the terms of any separate license agreement you may have executed
141 | with Licensor regarding such Contributions.
142 |
143 | 6. Trademarks. This License does not grant permission to use the trade
144 | names, trademarks, service marks, or product names of the Licensor,
145 | except as required for reasonable and customary use in describing the
146 | origin of the Work and reproducing the content of the NOTICE file.
147 |
148 | 7. Disclaimer of Warranty. Unless required by applicable law or
149 | agreed to in writing, Licensor provides the Work (and each
150 | Contributor provides its Contributions) on an "AS IS" BASIS,
151 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
152 | implied, including, without limitation, any warranties or conditions
153 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
154 | PARTICULAR PURPOSE. You are solely responsible for determining the
155 | appropriateness of using or redistributing the Work and assume any
156 | risks associated with Your exercise of permissions under this License.
157 |
158 | 8. Limitation of Liability. In no event and under no legal theory,
159 | whether in tort (including negligence), contract, or otherwise,
160 | unless required by applicable law (such as deliberate and grossly
161 | negligent acts) or agreed to in writing, shall any Contributor be
162 | liable to You for damages, including any direct, indirect, special,
163 | incidental, or consequential damages of any character arising as a
164 | result of this License or out of the use or inability to use the
165 | Work (including but not limited to damages for loss of goodwill,
166 | work stoppage, computer failure or malfunction, or any and all
167 | other commercial damages or losses), even if such Contributor
168 | has been advised of the possibility of such damages.
169 |
170 | 9. Accepting Warranty or Additional Liability. While redistributing
171 | the Work or Derivative Works thereof, You may choose to offer,
172 | and charge a fee for, acceptance of support, warranty, indemnity,
173 | or other liability obligations and/or rights consistent with this
174 | License. However, in accepting such obligations, You may act only
175 | on Your own behalf and on Your sole responsibility, not on behalf
176 | of any other Contributor, and only if You agree to indemnify,
177 | defend, and hold each Contributor harmless for any liability
178 | incurred by, or claims asserted against, such Contributor by reason
179 | of your accepting any such warranty or additional liability.
180 |
181 | END OF TERMS AND CONDITIONS
182 |
183 | APPENDIX: How to apply the Apache License to your work.
184 |
185 | To apply the Apache License to your work, attach the following
186 | boilerplate notice, with the fields enclosed by brackets "[]"
187 | replaced with your own identifying information. (Don't include
188 | the brackets!) The text should be enclosed in the appropriate
189 | comment syntax for the file format. We also recommend that a
190 | file or class name and description of purpose be included on the
191 | same "printed page" as the copyright notice for easier
192 | identification within third-party archives.
193 |
194 | Copyright [yyyy] [name of copyright owner]
195 |
196 | Licensed under the Apache License, Version 2.0 (the "License");
197 | you may not use this file except in compliance with the License.
198 | You may obtain a copy of the License at
199 |
200 | http://www.apache.org/licenses/LICENSE-2.0
201 |
202 | Unless required by applicable law or agreed to in writing, software
203 | distributed under the License is distributed on an "AS IS" BASIS,
204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
205 | See the License for the specific language governing permissions and
206 | limitations under the License.
207 | *******************************************************************************
--------------------------------------------------------------------------------
/AtomicLib/utilities/android/apktool.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/AtomicLib/utilities/android/apktool.jar
--------------------------------------------------------------------------------
/AtomicLib/utilities/xdelta/LICENSE:
--------------------------------------------------------------------------------
1 | Xdelta version 3.x is licensed under the Apache Public License version 2.0: http://www.apache.org/licenses/LICENSE-2.0
2 |
3 |
4 | Apache License
5 | Version 2.0, January 2004
6 | http://www.apache.org/licenses/
7 |
8 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
9 |
10 | 1. Definitions.
11 |
12 | "License" shall mean the terms and conditions for use, reproduction,
13 | and distribution as defined by Sections 1 through 9 of this document.
14 |
15 | "Licensor" shall mean the copyright owner or entity authorized by
16 | the copyright owner that is granting the License.
17 |
18 | "Legal Entity" shall mean the union of the acting entity and all
19 | other entities that control, are controlled by, or are under common
20 | control with that entity. For the purposes of this definition,
21 | "control" means (i) the power, direct or indirect, to cause the
22 | direction or management of such entity, whether by contract or
23 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
24 | outstanding shares, or (iii) beneficial ownership of such entity.
25 |
26 | "You" (or "Your") shall mean an individual or Legal Entity
27 | exercising permissions granted by this License.
28 |
29 | "Source" form shall mean the preferred form for making modifications,
30 | including but not limited to software source code, documentation
31 | source, and configuration files.
32 |
33 | "Object" form shall mean any form resulting from mechanical
34 | transformation or translation of a Source form, including but
35 | not limited to compiled object code, generated documentation,
36 | and conversions to other media types.
37 |
38 | "Work" shall mean the work of authorship, whether in Source or
39 | Object form, made available under the License, as indicated by a
40 | copyright notice that is included in or attached to the work
41 | (an example is provided in the Appendix below).
42 |
43 | "Derivative Works" shall mean any work, whether in Source or Object
44 | form, that is based on (or derived from) the Work and for which the
45 | editorial revisions, annotations, elaborations, or other modifications
46 | represent, as a whole, an original work of authorship. For the purposes
47 | of this License, Derivative Works shall not include works that remain
48 | separable from, or merely link (or bind by name) to the interfaces of,
49 | the Work and Derivative Works thereof.
50 |
51 | "Contribution" shall mean any work of authorship, including
52 | the original version of the Work and any modifications or additions
53 | to that Work or Derivative Works thereof, that is intentionally
54 | submitted to Licensor for inclusion in the Work by the copyright owner
55 | or by an individual or Legal Entity authorized to submit on behalf of
56 | the copyright owner. For the purposes of this definition, "submitted"
57 | means any form of electronic, verbal, or written communication sent
58 | to the Licensor or its representatives, including but not limited to
59 | communication on electronic mailing lists, source code control systems,
60 | and issue tracking systems that are managed by, or on behalf of, the
61 | Licensor for the purpose of discussing and improving the Work, but
62 | excluding communication that is conspicuously marked or otherwise
63 | designated in writing by the copyright owner as "Not a Contribution."
64 |
65 | "Contributor" shall mean Licensor and any individual or Legal Entity
66 | on behalf of whom a Contribution has been received by Licensor and
67 | subsequently incorporated within the Work.
68 |
69 | 2. Grant of Copyright License. Subject to the terms and conditions of
70 | this License, each Contributor hereby grants to You a perpetual,
71 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
72 | copyright license to reproduce, prepare Derivative Works of,
73 | publicly display, publicly perform, sublicense, and distribute the
74 | Work and such Derivative Works in Source or Object form.
75 |
76 | 3. Grant of Patent License. Subject to the terms and conditions of
77 | this License, each Contributor hereby grants to You a perpetual,
78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
79 | (except as stated in this section) patent license to make, have made,
80 | use, offer to sell, sell, import, and otherwise transfer the Work,
81 | where such license applies only to those patent claims licensable
82 | by such Contributor that are necessarily infringed by their
83 | Contribution(s) alone or by combination of their Contribution(s)
84 | with the Work to which such Contribution(s) was submitted. If You
85 | institute patent litigation against any entity (including a
86 | cross-claim or counterclaim in a lawsuit) alleging that the Work
87 | or a Contribution incorporated within the Work constitutes direct
88 | or contributory patent infringement, then any patent licenses
89 | granted to You under this License for that Work shall terminate
90 | as of the date such litigation is filed.
91 |
92 | 4. Redistribution. You may reproduce and distribute copies of the
93 | Work or Derivative Works thereof in any medium, with or without
94 | modifications, and in Source or Object form, provided that You
95 | meet the following conditions:
96 |
97 | (a) You must give any other recipients of the Work or
98 | Derivative Works a copy of this License; and
99 |
100 | (b) You must cause any modified files to carry prominent notices
101 | stating that You changed the files; and
102 |
103 | (c) You must retain, in the Source form of any Derivative Works
104 | that You distribute, all copyright, patent, trademark, and
105 | attribution notices from the Source form of the Work,
106 | excluding those notices that do not pertain to any part of
107 | the Derivative Works; and
108 |
109 | (d) If the Work includes a "NOTICE" text file as part of its
110 | distribution, then any Derivative Works that You distribute must
111 | include a readable copy of the attribution notices contained
112 | within such NOTICE file, excluding those notices that do not
113 | pertain to any part of the Derivative Works, in at least one
114 | of the following places: within a NOTICE text file distributed
115 | as part of the Derivative Works; within the Source form or
116 | documentation, if provided along with the Derivative Works; or,
117 | within a display generated by the Derivative Works, if and
118 | wherever such third-party notices normally appear. The contents
119 | of the NOTICE file are for informational purposes only and
120 | do not modify the License. You may add Your own attribution
121 | notices within Derivative Works that You distribute, alongside
122 | or as an addendum to the NOTICE text from the Work, provided
123 | that such additional attribution notices cannot be construed
124 | as modifying the License.
125 |
126 | You may add Your own copyright statement to Your modifications and
127 | may provide additional or different license terms and conditions
128 | for use, reproduction, or distribution of Your modifications, or
129 | for any such Derivative Works as a whole, provided Your use,
130 | reproduction, and distribution of the Work otherwise complies with
131 | the conditions stated in this License.
132 |
133 | 5. Submission of Contributions. Unless You explicitly state otherwise,
134 | any Contribution intentionally submitted for inclusion in the Work
135 | by You to the Licensor shall be under the terms and conditions of
136 | this License, without any additional terms or conditions.
137 | Notwithstanding the above, nothing herein shall supersede or modify
138 | the terms of any separate license agreement you may have executed
139 | with Licensor regarding such Contributions.
140 |
141 | 6. Trademarks. This License does not grant permission to use the trade
142 | names, trademarks, service marks, or product names of the Licensor,
143 | except as required for reasonable and customary use in describing the
144 | origin of the Work and reproducing the content of the NOTICE file.
145 |
146 | 7. Disclaimer of Warranty. Unless required by applicable law or
147 | agreed to in writing, Licensor provides the Work (and each
148 | Contributor provides its Contributions) on an "AS IS" BASIS,
149 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
150 | implied, including, without limitation, any warranties or conditions
151 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
152 | PARTICULAR PURPOSE. You are solely responsible for determining the
153 | appropriateness of using or redistributing the Work and assume any
154 | risks associated with Your exercise of permissions under this License.
155 |
156 | 8. Limitation of Liability. In no event and under no legal theory,
157 | whether in tort (including negligence), contract, or otherwise,
158 | unless required by applicable law (such as deliberate and grossly
159 | negligent acts) or agreed to in writing, shall any Contributor be
160 | liable to You for damages, including any direct, indirect, special,
161 | incidental, or consequential damages of any character arising as a
162 | result of this License or out of the use or inability to use the
163 | Work (including but not limited to damages for loss of goodwill,
164 | work stoppage, computer failure or malfunction, or any and all
165 | other commercial damages or losses), even if such Contributor
166 | has been advised of the possibility of such damages.
167 |
168 | 9. Accepting Warranty or Additional Liability. While redistributing
169 | the Work or Derivative Works thereof, You may choose to offer,
170 | and charge a fee for, acceptance of support, warranty, indemnity,
171 | or other liability obligations and/or rights consistent with this
172 | License. However, in accepting such obligations, You may act only
173 | on Your own behalf and on Your sole responsibility, not on behalf
174 | of any other Contributor, and only if You agree to indemnify,
175 | defend, and hold each Contributor harmless for any liability
176 | incurred by, or claims asserted against, such Contributor by reason
177 | of your accepting any such warranty or additional liability.
178 |
179 | END OF TERMS AND CONDITIONS
180 |
181 | APPENDIX: How to apply the Apache License to your work.
182 |
183 | To apply the Apache License to your work, attach the following
184 | boilerplate notice, with the fields enclosed by brackets "[]"
185 | replaced with your own identifying information. (Don't include
186 | the brackets!) The text should be enclosed in the appropriate
187 | comment syntax for the file format. We also recommend that a
188 | file or class name and description of purpose be included on the
189 | same "printed page" as the copyright notice for easier
190 | identification within third-party archives.
191 |
192 | Copyright [yyyy] [name of copyright owner]
193 |
194 | Licensed under the Apache License, Version 2.0 (the "License");
195 | you may not use this file except in compliance with the License.
196 | You may obtain a copy of the License at
197 |
198 | http://www.apache.org/licenses/LICENSE-2.0
199 |
200 | Unless required by applicable law or agreed to in writing, software
201 | distributed under the License is distributed on an "AS IS" BASIS,
202 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
203 | See the License for the specific language governing permissions and
204 | limitations under the License.
--------------------------------------------------------------------------------
/AtomicLib/utilities/xdelta/xdelta3.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/AtomicLib/utilities/xdelta/xdelta3.exe
--------------------------------------------------------------------------------
/AtomicLibTests/AtomicLibTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 | all
17 |
18 |
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 | all
21 |
22 |
23 |
24 |
25 |
26 |
27 | PreserveNewest
28 |
29 |
30 | PreserveNewest
31 |
32 |
33 | PreserveNewest
34 |
35 |
36 | PreserveNewest
37 |
38 |
39 | PreserveNewest
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/AtomicLibTests/CoreTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Compression;
2 | using AtomicLib;
3 | using AtomicLib.XML;
4 | using Xunit;
5 | using Xunit.Abstractions;
6 |
7 | namespace AtomicLibTests;
8 |
9 | public class CoreTests : IDisposable
10 | {
11 | private string am2r_11Path;
12 | private ITestOutputHelper console;
13 | private string testTempDir;
14 |
15 | public CoreTests(ITestOutputHelper output)
16 | {
17 | console = output;
18 | testTempDir = Path.GetTempPath() + Guid.NewGuid() + "/";
19 | // TODO: use a custom "AM2R_11" which is just a modified am2r_sever. the backend doesnt check for 1.1 validity, so we can do that.
20 | Directory.CreateDirectory(testTempDir);
21 | var am2rEnvVar = Environment.GetEnvironmentVariable("AM2R_11PATH");
22 | if (am2rEnvVar is not null && File.Exists(am2rEnvVar))
23 | {
24 | am2r_11Path = am2rEnvVar;
25 | return;
26 | }
27 |
28 | if (File.Exists("AM2R_11.zip"))
29 | {
30 | am2r_11Path = "AM2R_11.zip";
31 | return;
32 | }
33 |
34 | Assert.Fail("AM2R 1.1 file could not be found! Please place it into PWD or provide it via the AM2R_11PATH environment variable.");
35 | }
36 |
37 | public void Dispose()
38 | {
39 | Directory.Delete(testTempDir, true);
40 | }
41 |
42 | [Fact]
43 | public void CreateModPack_ShouldThrowWithNullModInfo()
44 | {
45 | Assert.Throws(() => Core.CreateModPack(null, testTempDir + "foo.zip"));
46 | }
47 |
48 | [Fact]
49 | public void CreateModPack_ShouldThrowWithNullModInfoProfile()
50 | {
51 | var modInfo = new ModCreationInfo();
52 | modInfo.Profile = null;
53 | Assert.Throws(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
54 | }
55 |
56 | [Fact]
57 | public void CreateModPack_ShouldThrowWithAM2R11PathNotSet()
58 | {
59 | var modInfo = new ModCreationInfo();
60 | modInfo.Profile = new ModProfileXML();
61 | modInfo.Profile.OperatingSystem = "Windows";
62 |
63 | Assert.Throws(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
64 | }
65 |
66 | [Fact]
67 | public void CreateModPack_ShouldThrowWithUnknownProfileOS()
68 | {
69 | var modInfo = new ModCreationInfo();
70 | modInfo.Profile = new ModProfileXML();
71 | modInfo.Profile.OperatingSystem = "asdfasdf";
72 | modInfo.AM2R11Path = am2r_11Path;
73 |
74 | Assert.Throws(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
75 | }
76 |
77 | [Fact]
78 | public void CreateModPack_ShouldThrowWhenProfileSetButNotOSpath()
79 | {
80 | var modInfo = new ModCreationInfo();
81 | modInfo.Profile = new ModProfileXML();
82 | modInfo.Profile.OperatingSystem = "Windows";
83 | modInfo.AM2R11Path = am2r_11Path;
84 |
85 | Assert.Throws(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
86 | }
87 |
88 | [Fact]
89 | public void CreateModPack_ShouldThrowWhenAndroidIsMarkedAsSupportedButPathDoesNotExist()
90 | {
91 | var modInfo = new ModCreationInfo();
92 | modInfo.Profile = new ModProfileXML();
93 | modInfo.Profile.OperatingSystem = "Windows";
94 | modInfo.Profile.SupportsAndroid = true;
95 | modInfo.AM2R11Path = am2r_11Path;
96 |
97 | Assert.Throws(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
98 | }
99 |
100 | [Theory]
101 | [InlineData("Windows", false, false, false)]
102 | [InlineData("Windows", false, false, true)]
103 | [InlineData("Windows", false, true, false)]
104 | [InlineData("Windows", false, true, true)]
105 | [InlineData("Windows", true, false, false)]
106 | [InlineData("Windows", true, false, true)]
107 | [InlineData("Windows", true, true, false)]
108 | [InlineData("Windows", true, true, true)]
109 | [InlineData("Linux", false, false, false)]
110 | [InlineData("Linux", false, false, true)]
111 | [InlineData("Linux", false, true, false)]
112 | [InlineData("Linux", false, true, true)]
113 | [InlineData("Linux", true, false, false)]
114 | [InlineData("Linux", true, false, true)]
115 | [InlineData("Linux", true, true, false)]
116 | [InlineData("Linux", true, true, true)]
117 | [InlineData("Mac", false, false, false)]
118 | [InlineData("Mac", false, false, true)]
119 | [InlineData("Mac", false, true, false)]
120 | [InlineData("Mac", false, true, true)]
121 | [InlineData("Mac", true, false, false)]
122 | [InlineData("Mac", true, false, true)]
123 | [InlineData("Mac", true, true, false)]
124 | [InlineData("Mac", true, true, true)]
125 | public void CreateModPack_AllOptionsShouldCauseValidModpacks(string operatingSystem, bool usesCustomMusic, bool supportsAndroid, bool isYYC)
126 | {
127 | var modInfo = new ModCreationInfo();
128 | modInfo.Profile = new ModProfileXML();
129 | modInfo.Profile.OperatingSystem = operatingSystem;
130 | modInfo.Profile.UsesCustomMusic = usesCustomMusic;
131 | modInfo.Profile.SupportsAndroid = supportsAndroid;
132 | if (supportsAndroid)
133 | modInfo.ApkModPath = "GameAndroid.apk";
134 | modInfo.Profile.UsesYYC = isYYC;
135 | modInfo.Profile.Name = "Cool Mod";
136 | modInfo.Profile.Version = "cool version";
137 | modInfo.Profile.ProfileNotes = "This is my very own cool mod";
138 | modInfo.AM2R11Path = am2r_11Path;
139 | switch (operatingSystem)
140 | {
141 | case "Windows": modInfo.WindowsModPath = "GameWin.zip"; break;
142 | case "Linux": modInfo.LinuxModPath = "GameLin.zip"; break;
143 | case "Mac": modInfo.MacModPath = "GameMac.zip"; break;
144 | default: Assert.Fail(nameof(CreateModPack_AllOptionsShouldCauseValidModpacks) + " was called with improper parameters?"); break;
145 | }
146 |
147 | Core.CreateModPack(modInfo, testTempDir + "foo.zip");
148 |
149 | // TODO: assert on proper packaging, by investigating contents of zip
150 | ZipArchive archive = ZipFile.OpenRead(testTempDir + "foo.zip");
151 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "profile.xml") is not null);
152 | // TODO: try to deserialize xml to check it has what we put in
153 | if (isYYC)
154 | {
155 | if (operatingSystem == "Windows")
156 | {
157 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "AM2R.xdelta") is not null);
158 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "game.xdelta") is null);
159 | }
160 | }
161 | else
162 | {
163 | // Unix has both in YYC and non-YYC
164 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "AM2R.xdelta") is not null);
165 | if (operatingSystem == "Windows")
166 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "data.xdelta") is not null);
167 | else
168 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "game.xdelta") is not null);
169 | }
170 |
171 | if (supportsAndroid)
172 | {
173 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "droid.xdelta") is not null);
174 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "android/AM2RWrapper.apk") is not null);
175 | if (isYYC)
176 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "android/AM2R.ini") is not null);
177 | // TODO: atomic currently always copies the file if it exists. Should it only copy it if we're dealing with yyc?
178 | //else
179 | //Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "android/AM2R.ini") is null);
180 | }
181 |
182 | if (operatingSystem is "Linux" or "Mac")
183 | {
184 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/icon.png") is not null);
185 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/splash.png") is not null);
186 | if (operatingSystem == "Mac")
187 | {
188 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "Info.plist") is not null);
189 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/yoyorunner.config") is not null);
190 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/gamecontrollerdb.txt") is not null);
191 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/english.lproj/MainMenu.nib") is not null);
192 | }
193 | }
194 | else
195 | {
196 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/icon.png") is null);
197 | Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/splash.png") is null);
198 | }
199 |
200 | if (usesCustomMusic)
201 | {
202 | // TODO: check for custom music. do that after we provided a fake am2r_11.
203 | }
204 | else
205 | {
206 | // TODO: make sure that if we're providing lowercase songs, that they'll be also blacklisted.
207 | }
208 | }
209 |
210 | [Fact]
211 | public void CreateModPack_InvalidOSShouldThrow()
212 | {
213 | var modInfo = new ModCreationInfo();
214 | modInfo.Profile = new ModProfileXML();
215 | modInfo.Profile.OperatingSystem = "foobar";
216 | modInfo.Profile.Name = "Cool Mod";
217 | modInfo.Profile.Version = "cool version";
218 | modInfo.Profile.ProfileNotes = "This is my very own cool mod";
219 | modInfo.AM2R11Path = am2r_11Path;
220 |
221 | Assert.Throws(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
222 | }
223 |
224 | [Fact]
225 | public void CreateModPack_ShouldCleanUpDirectoryIfItExistedBefore()
226 | {
227 | var modInfo = new ModCreationInfo();
228 | modInfo.Profile = new ModProfileXML();
229 | modInfo.Profile.OperatingSystem = "Windows";
230 | modInfo.Profile.UsesCustomMusic = false;
231 | modInfo.Profile.SupportsAndroid = false;
232 | modInfo.Profile.UsesYYC = false;
233 | modInfo.Profile.Name = "Cool Mod";
234 | modInfo.Profile.Version = "cool version";
235 | modInfo.Profile.ProfileNotes = "This is my very own cool mod";
236 | modInfo.WindowsModPath = "GameWin.zip";
237 | modInfo.AM2R11Path = am2r_11Path;
238 |
239 | string tempPath = Path.GetTempPath() + "/Atomic";
240 | Directory.CreateDirectory(tempPath);
241 | Assert.True(Directory.Exists(tempPath));
242 |
243 | Core.CreateModPack(modInfo, testTempDir + "foo.zip");
244 |
245 | Assert.False(Directory.Exists(tempPath));
246 | }
247 |
248 | [Fact]
249 | public void CreateModPack_ShouldCleanUpOutputFileIfItExistedBefore()
250 | {
251 | var modInfo = new ModCreationInfo();
252 | modInfo.Profile = new ModProfileXML();
253 | modInfo.Profile.OperatingSystem = "Windows";
254 | modInfo.Profile.UsesCustomMusic = false;
255 | modInfo.Profile.SupportsAndroid = false;
256 | modInfo.Profile.UsesYYC = false;
257 | modInfo.Profile.Name = "Cool Mod";
258 | modInfo.Profile.Version = "cool version";
259 | modInfo.Profile.ProfileNotes = "This is my very own cool mod";
260 | modInfo.WindowsModPath = "GameWin.zip";
261 | modInfo.AM2R11Path = am2r_11Path;
262 |
263 | string tempPath = testTempDir + "foo.zip";
264 | File.WriteAllText(tempPath, "foobar");
265 | Assert.True(File.Exists(tempPath));
266 |
267 | Core.CreateModPack(modInfo, testTempDir + "foo.zip");
268 |
269 | Assert.True(File.Exists(tempPath));
270 | using var reader = new StreamReader(tempPath);
271 | Assert.False(reader.ReadLine() == "foobar");
272 | }
273 |
274 | [Fact]
275 | public void CreatePatch_ShouldThrowOnMissingXdelta()
276 | {
277 |
278 | var path = Environment.GetEnvironmentVariable("PATH");
279 | Environment.SetEnvironmentVariable("PATH", "");
280 | Assert.Throws(() => Core.CreatePatch("foo", "bar", "foobar"));
281 | Environment.SetEnvironmentVariable("PATH", path);
282 | }
283 |
284 | [Fact]
285 | public void RunJavaJar_ShouldThrowOnMissingJava()
286 | {
287 |
288 | var path = Environment.GetEnvironmentVariable("PATH");
289 | Environment.SetEnvironmentVariable("PATH", "");
290 | Assert.Throws(() => Core.RunJavaJar());
291 | Environment.SetEnvironmentVariable("PATH", path);
292 | }
293 | }
--------------------------------------------------------------------------------
/AtomicLibTests/GameAndroid.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/AtomicLibTests/GameAndroid.apk
--------------------------------------------------------------------------------
/AtomicLibTests/GameLin.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/AtomicLibTests/GameLin.zip
--------------------------------------------------------------------------------
/AtomicLibTests/GameMac.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/AtomicLibTests/GameMac.zip
--------------------------------------------------------------------------------
/AtomicLibTests/GameWin.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/AtomicLibTests/GameWin.zip
--------------------------------------------------------------------------------
/AtomicLibTests/LICENSE-AM2RServer.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 milesthenerd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6 |
7 |
8 | ## [Unreleased] - 202?-??-??
9 |
10 |
11 | ## [2.2.0] - 2024-03-17
12 |
13 | - Added: Show error messages if Atomic crashes
14 | - Added: Mod names will now have leading/trailing whitespace trimmed
15 | - Added: Localization Support. Atomic should be *mostly* localized into English, German, Japanese, Italian, Spanish, Portuguese, Chinese and Russian.
16 | - Added: Option to remember the last used field content
17 | - Changed: MessageBoxes will now make the main window inactive.
18 | - Changed: Atomic now requires dotnet8 instead of dotnet6
19 | - Fixed: macOS builds should now work
20 | - Fixed: Using custom save location now works when you have a non-C drive on Windows as your system drive
21 | - Fixed: Launching Atomic from a flatpak should now work.
22 |
23 | ## [2.1.0] - 2022-12-24
24 |
25 | - Added: A warning is shown if the mod zip contains any subfolders
26 | - Added: A warning is shown if the mod zip contains a `profile.xml` file.
27 | - Added: Better warnings are shown if an invalid AM2R 1.1.zip is provided.
28 | - Changed: The project has been rebranded from AM2RModPacker to Atomic. This includes having a custom icon.
29 | - Changed: The project has been rewritten in Eto.Forms instead of WinForms. This means that this project now also supports running on Linux and macOS.
30 | - Changed: The UI is now completely resizable.
31 | - Changed: The space between the left side and the right side in the UI is now adjustable via a splitter.
32 | - Changed: Don't allow the user to use invalid characters in their mod name.
33 | - Changed: The option to make Mods compatible with the experimental Mac Am2RLauncher was added.
34 | - Changed: Making a Mod compatible with the Windows AM2RLauncher is now not required anymore
35 | - Changed: Remember the last chosen folder for file dialogs.
36 | - Changed: If the save location of a user's mod is inside a subfolder of the vanilla save location, and that mod save location is selected, then the save location will be automatically corrected to use lowercase subfolders to mimic what Game Maker does.
37 | - Changed: When having a mod zip, that contains non-lowercase subfolders, then during mod creation these will now be lowercased to make the mods work correctly on Unix systems.
38 |
39 | ## [2.0.3] - 2021-05-2
40 |
41 | - Fixed: Small inconsistencies with custom save locations.
42 |
43 | ## [2.0.2] - 2021-04-17
44 |
45 | - Fixed: Issues when selecting VM builds from GameMaker.
46 |
47 |
48 | ## [2.0.1] - 2021-04-14
49 |
50 | - Fixed: Fixes a crash if AM2R.ini does not exist in the input APK for some reason (likely due to VM exports).
51 |
52 | ## [2.0.0] - 2021-03-26
53 |
54 | - Added: Better error handling if creating temporary directories fail.
55 | - Added: Supports creating Linux mods.
56 | - Added: Supports creating Android mods.
57 | - Added: Added new fields to the UI: A version field and a mod notes field
58 | - Added: Support for AM2RLauncher 2.0.0
59 | - Changed: Relicensed the project from MIT to GPLv3.
60 | - Changed: Improved the save directory field in the UI.
61 | - Changed: Generated mod info is now saved as XML instead of JSON.
62 | - Fixed: Fix rare cases where UI was still resizable.
63 | - Removed: Support for AM2RLauncher 1.X.X.
64 |
65 | ## [1.0.1]
66 |
67 | - Changed: Improved UI performance.
68 | - Changed: Limit Mod name and Author name to 30 characters.
69 | - Fixed: Disable resizing the UI.
70 | - Fixed: Fixed the original file picker dialog referring to the modded am2r zip, and vice versa.
71 |
72 | ## [1.0.0] - 2021-01-12
73 |
74 | - Added: Initial Release
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Atomic
2 | Atomic (AM2R Tool: Organized Modpacker in C#) is a mod packaging toolchain for [AM2RLauncher](https://github.com/AM2R-Community-Developers/AM2RLauncher) mods.
3 |
4 | This is for modcreators who wish to make their mods usable with the [AM2RLauncher](https://github.com/AM2R-Community-Developers/AM2RLauncher).
5 | It currently supports creating Windows, Linux, Mac and Android mods.
6 |
7 | For actual mod creation, it is recommended to use [UndertaleModTool](https://github.com/krzys-h/UndertaleModTool), but they can be created with Game Maker: Studio and the [AM2R Community Updates repository](https://github.com/AM2R-Community-Developers/AM2R-Community-Updates) as well.
8 |
9 | ## Usage instructions
10 | 
11 |
12 | * Mod name: Specify the name of the mod
13 | * Author: Specify the authors of the mod
14 | * Version: Specify the version of the mod
15 | * Mod notes (optional): Specify notes the users will see about the mod. Consider this like a readme.
16 | * Uses custom save directory: Specify whether or not your mod uses a custom save directory and if yes, which one. Only save locations in `%localappdata%` (and the equivalent on other OS) are supported
17 | * Uses custom music: Specify whether or not your mod has custom music in it.
18 | * Uses the YoYo Compiler: Specify whether or not your mod was built with the YoYo Compiler or not. A quick way to find out, is try opening the `data.win` file in the above mentioned [UndertaleModTool](https://github.com/krzys-h/UndertaleModTool). If the tool mentions that it was built with YYC, or the file does not exist, it's YYC.
19 | * Supports Windows: Specify whether or not your mod supports Windows, and if yes load in the modded Windows zip.
20 | * Supports Android: Specify whether or not your mod supports Android, and if yes load in the modded Android APK.
21 | * Supports Linux: Specify whether or not your mod supports Linux, and if yes load in the modded Linux zip. For GameMaker: Studio users, you can just use the one that gets created. For non GM:S users, please make sure that the executable is either named `AM2R` or `runner` and that neither the `assets` folder nor the executable is in a subfolder.
22 | * Supports Mac: Specify whether or not your mod supports Windows, and if yes load in the modded Mac zip.
23 | * Load 1.1: Select your valid 1.1 Zip. If you get an error, make sure that the game isn't in any subfolder.
24 |
25 | After filling everything out appropriately, just click on the `Create Mod package(s)` button, which will ask you where to save your AM2RLauncher-compatible mod and then create them.
26 |
--------------------------------------------------------------------------------
/distribution/linux/Atomic.appdata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | io.github.am2r_community_developers.Atomic
5 | CC0-1.0
6 | GPL-3.0
7 | Atomic
8 | AM2R Community Developers
9 | A mod packaging toolchain for AM2R mods.
10 |
11 | Utility
12 |
13 |
14 |
15 | Atomic (AM2R Tool: Organized Modpacker in C#) is a tool for modcreators who wish to make their mods usable with the AM2RLauncher.
16 | It currently supports creating Windows, Linux, Mac and Android mods.
17 |