├── .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 | ![screenshot of tool](https://user-images.githubusercontent.com/38186597/209388376-93c07910-89e3-4a4a-a501-d06b094915d4.png) 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 |

18 |
19 | io.github.am2r_community_developers.Atomic.desktop 20 | https://github.com/AM2R-Community-Developers/Atomic 21 | https://github.com/AM2R-Community-Developers/Atomic/issues 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Atomic main window 30 | https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/6b504f8aad23859f7053421b057b1bf1c546704e/distribution/linux/screenshot.png 31 | 32 | 33 |
34 | -------------------------------------------------------------------------------- /distribution/linux/Atomic.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Categories=Utility 4 | Encoding=UTF-8 5 | Name=Atomic 6 | Comment=A mod packaging toolchain for AM2R mods. 7 | Exec=atomic 8 | Icon=Atomic 9 | Terminal=false 10 | -------------------------------------------------------------------------------- /distribution/linux/Atomic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/distribution/linux/Atomic.png -------------------------------------------------------------------------------- /distribution/linux/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM2R-Community-Developers/Atomic/2c59c4cc8ab79842b09b6b8cdbf544a1ddccbb99/distribution/linux/screenshot.png --------------------------------------------------------------------------------