From b6893fe0ab2181b55b96f1aaedd4035a61e5da5b Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 2 May 2021 20:57:55 +0200 Subject: [PATCH] All the build updates --- .github/workflows/{wasm.yml => build.yml} | 39 +++++++++++++- .github/workflows/test.yml | 43 --------------- build.gradle | 51 ++++++++---------- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- rust/ddsaveedit/ddsavelib/src/file/mod.rs | 1 + .../de/robojumper/ddsavereader/Dson2Json.java | 2 +- .../de/robojumper/ddsavereader/Json2Dson.java | 2 +- .../java/de/robojumper/ddsavereader/Main.java | 2 +- .../ddsavereader/file/DsonFile.java | 4 +- .../ddsavereader/file/DsonTypes.java | 1 + .../ddsavereader/model/CampaignLog.java | 6 +-- .../robojumper/ddsavereader/model/Estate.java | 3 +- .../robojumper/ddsavereader/model/Roster.java | 5 +- .../robojumper/ddsavereader/model/Town.java | 6 +-- .../spreadsheets/SpreadsheetsService.java | 6 +-- .../ddsavereader/ui/DataPathsDialog.java | 4 +- .../ddsavereader/ui/MainWindow.java | 17 +++--- .../de/robojumper/ddsavereader/ui/State.java | 6 +-- .../updatechecker/UpdateChecker.java | 5 +- .../robojumper/ddsavereader/util/Helpers.java | 2 +- .../ddsavereader/util/ReadNames.java | 13 ++--- .../ddsavereader/file/ConverterTests.java | 1 + .../persist.raid.json | Bin 0 -> 39652 bytes 24 files changed, 102 insertions(+), 121 deletions(-) rename .github/workflows/{wasm.yml => build.yml} (53%) delete mode 100644 .github/workflows/test.yml create mode 100644 src/test/resources/valid_additional_mash_entry_indexes/persist.raid.json diff --git a/.github/workflows/wasm.yml b/.github/workflows/build.yml similarity index 53% rename from .github/workflows/wasm.yml rename to .github/workflows/build.yml index ac4c1fa..e736302 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/build.yml @@ -1,13 +1,18 @@ -name: wasm +name: build on: push: branches: - master + pull_request: + branches: + - master jobs: - build: + rust: runs-on: ubuntu-latest + env: + RUSTFLAGS: -D warnings steps: - name: Checkout uses: actions/checkout@v2 @@ -17,6 +22,12 @@ jobs: toolchain: nightly-2021-04-25 override: true profile: minimal + - name: Test + run: | + cd rust/ddsaveedit + cargo check + cargo test + cargo test --release - name: Install Node uses: actions/setup-node@v1 with: @@ -31,9 +42,33 @@ jobs: npm run build cd ../../../ - name: Deploy + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} uses: crazy-max/ghaction-github-pages@v2 with: target_branch: gh-pages build_dir: rust/ddsaveedit/wasm-ddsaveedit/dist # The folder the action should deploy. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + + java: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Install Java + uses: actions/setup-java@v1 + with: + java-version: '8' + java-package: jdk + - name: Test + run: | + ./gradlew test + - name: Build distribution zip + run: | + ./gradlew dist + - name: Upload distribution zip + uses: actions/upload-artifact@v1 + with: + name: DDSaveEditor + path: build/dist/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 42ffd28..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: test - -on: - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - test-rust: - runs-on: ubuntu-latest - env: - RUSTFLAGS: -D warnings - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-2021-04-25 - override: true - profile: minimal - - name: Test - run: | - cd rust/ddsaveedit - cargo check - cargo test - cargo test --release - test-java: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install Java - uses: actions/setup-java@v1 - with: - java-version: '8' - java-package: jdk - - name: Test - run: | - ./gradlew test diff --git a/build.gradle b/build.gradle index 9ffc097..a64e176 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,11 @@ plugins { - id 'de.fuerstenau.buildconfig' version '1.1.8' + id 'java' + id 'application' + id 'com.github.gmazzo.buildconfig' version '3.0.0' } - -apply plugin: 'java' -apply plugin: 'application' -apply plugin: 'eclipse' - repositories { mavenCentral() - jcenter() } sourceCompatibility = 1.8 @@ -17,28 +13,25 @@ targetCompatibility = 1.8 dependencies { - testCompile 'org.testng:testng:6.8.8' - compile 'com.google.code.gson:gson:2.8.5' - compile 'com.google.api-client:google-api-client:1.23.0' - compile 'com.google.oauth-client:google-oauth-client-jetty:1.23.0' - compile 'com.google.apis:google-api-services-sheets:v4-rev516-1.23.0' - compile 'com.fifesoft:rsyntaxtextarea:3.0.3' - compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.6' + testImplementation 'org.testng:testng:7.4.0' + implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.google.api-client:google-api-client:1.31.2' + implementation 'com.google.oauth-client:google-oauth-client-jetty:1.31.2' + implementation 'com.google.apis:google-api-services-sheets:v4-rev612-1.25.0' + implementation 'com.fifesoft:rsyntaxtextarea:3.1.2' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.3' } buildConfig { - appName = project.name - version = project.version - - buildConfigField 'String', 'DISPLAY_NAME', "$displayname" - buildConfigField 'String', 'GITHUB_URL', "$githuburl" - buildConfigField 'String', 'UPDATE_URL', "$releasesurl" - buildConfigField 'String', 'JAR_NAME', "${jarname}.jar" - buildConfigField 'String', 'DATA_DIR', "$datadir" + packageName("de.robojumper.ddsavereader") + buildConfigField('String', 'NAME', "\"${project.name}\"") + buildConfigField('String', 'VERSION', "\"${project.version}\"") - clsName = 'BuildConfig' - packageName = project.group - charset = 'UTF-8' + buildConfigField('String', 'DISPLAY_NAME', "\"$displayname\"") + buildConfigField('String', 'GITHUB_URL', "\"$githuburl\"") + buildConfigField('String', 'UPDATE_URL', "\"$releasesurl\"") + buildConfigField('String', 'JAR_NAME', "\"${jarname}.jar\"") + buildConfigField('String', 'DATA_DIR', "\"$datadir\"") } test { @@ -61,9 +54,10 @@ task fatJar(type: Jar) { 'Implementation-Version': project.version, 'Main-Class': mainClassName } + setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) archiveBaseName = "$jarname" archiveFileName = "${jarname}.jar" - from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } with jar } @@ -84,6 +78,7 @@ task dist(type: Zip, dependsOn: fatJar) { include "**/*" into "docs" } - destinationDir = file("${buildDir}/dist") - baseName = "${jarname}" + + destinationDirectory = file("${buildDir}/dist") + archiveBaseName = "${jarname}" } diff --git a/gradle.properties b/gradle.properties index 469debf..bcf5b53 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ displayname=Darkest Dungeon Save Editor -version=v0.0.69 +version=v0.0.70 githuburl=https://github.com/robojumper/DarkestDungeonSaveEditor releasesurl=https://api.github.com/repos/robojumper/DarkestDungeonSaveEditor/releases/latest jarname=DDSaveEditor diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f4d7b2b..f371643 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/rust/ddsaveedit/ddsavelib/src/file/mod.rs b/rust/ddsaveedit/ddsavelib/src/file/mod.rs index 20f82cc..e6bd262 100644 --- a/rust/ddsaveedit/ddsavelib/src/file/mod.rs +++ b/rust/ddsaveedit/ddsavelib/src/file/mod.rs @@ -421,6 +421,7 @@ fn hardcoded_type(parents: &'_ [impl AsRef], name: impl AsRef) -> Opti [IntVector, "result_event_history"], [IntVector, "dead_hero_entries"], [IntVector, "additional_mash_disabled_infestation_monster_class_ids"], + [IntVector, "mash", "valid_additional_mash_entry_indexes"], [IntVector, "party", "heroes"], [IntVector, "skill_cooldown_keys"], [IntVector, "skill_cooldown_values"], diff --git a/src/main/java/de/robojumper/ddsavereader/Dson2Json.java b/src/main/java/de/robojumper/ddsavereader/Dson2Json.java index 39c490e..dc7de55 100644 --- a/src/main/java/de/robojumper/ddsavereader/Dson2Json.java +++ b/src/main/java/de/robojumper/ddsavereader/Dson2Json.java @@ -8,7 +8,7 @@ import java.nio.file.Files; import java.nio.file.Paths; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import de.robojumper.ddsavereader.file.DsonFile; import de.robojumper.ddsavereader.file.DsonTypes; import de.robojumper.ddsavereader.file.DsonFile.UnhashBehavior; diff --git a/src/main/java/de/robojumper/ddsavereader/Json2Dson.java b/src/main/java/de/robojumper/ddsavereader/Json2Dson.java index fc25755..2dcdfef 100644 --- a/src/main/java/de/robojumper/ddsavereader/Json2Dson.java +++ b/src/main/java/de/robojumper/ddsavereader/Json2Dson.java @@ -5,7 +5,7 @@ import java.nio.file.Paths; import java.text.ParseException; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import de.robojumper.ddsavereader.file.DsonWriter; public class Json2Dson { diff --git a/src/main/java/de/robojumper/ddsavereader/Main.java b/src/main/java/de/robojumper/ddsavereader/Main.java index 5e1de13..681510b 100644 --- a/src/main/java/de/robojumper/ddsavereader/Main.java +++ b/src/main/java/de/robojumper/ddsavereader/Main.java @@ -1,6 +1,6 @@ package de.robojumper.ddsavereader; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import de.robojumper.ddsavereader.spreadsheets.SpreadsheetsService; import de.robojumper.ddsavereader.ui.MainWindow; import de.robojumper.ddsavereader.util.ReadNames; diff --git a/src/main/java/de/robojumper/ddsavereader/file/DsonFile.java b/src/main/java/de/robojumper/ddsavereader/file/DsonFile.java index f4b5642..f5547d3 100644 --- a/src/main/java/de/robojumper/ddsavereader/file/DsonFile.java +++ b/src/main/java/de/robojumper/ddsavereader/file/DsonFile.java @@ -99,7 +99,7 @@ public DsonFile(byte[] File, UnhashBehavior behavior) throws ParseException { Stack parentIdxStack = new Stack(); // base_root starts at -1 int runningObjIdx = -1; - parentIdxStack.push(new Integer(runningObjIdx)); + parentIdxStack.push(Integer.valueOf(runningObjIdx)); rootFields = new ArrayList(); // Is this the correct way to do it? // WARNING: Apparently, META2 is not necessarily ordered the same way as DATA @@ -180,7 +180,7 @@ public DsonFile(byte[] File, UnhashBehavior behavior) throws ParseException { // If we have an object, push it to the stack if (field.type == FieldType.TYPE_OBJECT) { fieldStack.push(field); - parentIdxStack.push(new Integer(runningObjIdx)); + parentIdxStack.push(Integer.valueOf(runningObjIdx)); } // Then check if the object on top of the stack has all its children. If so, pop diff --git a/src/main/java/de/robojumper/ddsavereader/file/DsonTypes.java b/src/main/java/de/robojumper/ddsavereader/file/DsonTypes.java index 6e7495f..6ee512a 100644 --- a/src/main/java/de/robojumper/ddsavereader/file/DsonTypes.java +++ b/src/main/java/de/robojumper/ddsavereader/file/DsonTypes.java @@ -37,6 +37,7 @@ public enum FieldType { { "result_event_history" }, // town_event.json { "dead_hero_entries" }, // town_event.json { "additional_mash_disabled_infestation_monster_class_ids" }, // campaign_mash.json + { "mash", "valid_additional_mash_entry_indexes" }, // raid.json { "party", "heroes" }, // raid.json { "skill_cooldown_keys" }, // raid.json { "skill_cooldown_values" }, diff --git a/src/main/java/de/robojumper/ddsavereader/model/CampaignLog.java b/src/main/java/de/robojumper/ddsavereader/model/CampaignLog.java index 9cd31c6..da8ec49 100644 --- a/src/main/java/de/robojumper/ddsavereader/model/CampaignLog.java +++ b/src/main/java/de/robojumper/ddsavereader/model/CampaignLog.java @@ -32,8 +32,7 @@ public void write(JsonWriter out, Chapter value) throws IOException { @Override public Chapter read(JsonReader in) throws IOException { - JsonParser p = new JsonParser(); - JsonObject chapterRoot = p.parse(in).getAsJsonObject(); + JsonObject chapterRoot = JsonParser.parseReader(in).getAsJsonObject(); Chapter c = new Chapter(); c.chapterIndex = chapterRoot.getAsJsonPrimitive("chapterIndex").getAsInt(); Gson g = SaveState.makeGson(); @@ -189,8 +188,7 @@ public List getCells() { @Override public void update(String json) { - JsonParser parser = new JsonParser(); - JsonObject o = parser.parse(json).getAsJsonObject(); + JsonObject o = JsonParser.parseString(json).getAsJsonObject(); o = o.getAsJsonObject("base_root"); Gson g = SaveState.makeGson(); diff --git a/src/main/java/de/robojumper/ddsavereader/model/Estate.java b/src/main/java/de/robojumper/ddsavereader/model/Estate.java index 636563c..d2297b8 100644 --- a/src/main/java/de/robojumper/ddsavereader/model/Estate.java +++ b/src/main/java/de/robojumper/ddsavereader/model/Estate.java @@ -61,8 +61,7 @@ class ItemEntry { @Override public void update(String json) { - JsonParser parser = new JsonParser(); - JsonObject o = parser.parse(json).getAsJsonObject(); + JsonObject o = JsonParser.parseString(json).getAsJsonObject(); o = o.getAsJsonObject("base_root"); Gson g = SaveState.makeGson(); diff --git a/src/main/java/de/robojumper/ddsavereader/model/Roster.java b/src/main/java/de/robojumper/ddsavereader/model/Roster.java index 38674c8..01001e7 100644 --- a/src/main/java/de/robojumper/ddsavereader/model/Roster.java +++ b/src/main/java/de/robojumper/ddsavereader/model/Roster.java @@ -30,7 +30,7 @@ public Map read(JsonReader in) throws IOException { Map map = new HashMap<>(); in.beginObject(); while (in.peek() != JsonToken.END_OBJECT) { - int id = new Integer(in.nextName()); + int id = Integer.valueOf(in.nextName()); in.beginObject(); if (!Objects.equals(in.nextName(), "hero_file_data")) @@ -94,8 +94,7 @@ class RosterData { @Override public void update(String json) { - JsonParser parser = new JsonParser(); - JsonObject o = parser.parse(json).getAsJsonObject(); + JsonObject o = JsonParser.parseString(json).getAsJsonObject(); o = o.getAsJsonObject("base_root"); Gson g = SaveState.makeGson(); diff --git a/src/main/java/de/robojumper/ddsavereader/model/Town.java b/src/main/java/de/robojumper/ddsavereader/model/Town.java index 7cf8018..bceca4e 100644 --- a/src/main/java/de/robojumper/ddsavereader/model/Town.java +++ b/src/main/java/de/robojumper/ddsavereader/model/Town.java @@ -24,8 +24,7 @@ public static class BuildingActivitiesTypeAdapter extends TypeAdapter read(JsonReader in) throws IOException { Map map = new LinkedTreeMap<>(); - JsonParser p = new JsonParser(); - JsonObject o = p.parse(in).getAsJsonObject(); + JsonObject o = JsonParser.parseReader(in).getAsJsonObject(); for (Entry e : o.entrySet()) { String activityName = e.getKey(); for (Entry slot : e.getValue().getAsJsonObject().entrySet()) { @@ -78,8 +77,7 @@ class District { @Override public void update(String json) { - JsonParser parser = new JsonParser(); - JsonObject o = parser.parse(json).getAsJsonObject(); + JsonObject o = JsonParser.parseString(json).getAsJsonObject(); o = o.getAsJsonObject("base_root"); Gson g = SaveState.makeGson(); diff --git a/src/main/java/de/robojumper/ddsavereader/spreadsheets/SpreadsheetsService.java b/src/main/java/de/robojumper/ddsavereader/spreadsheets/SpreadsheetsService.java index 179b311..338109e 100644 --- a/src/main/java/de/robojumper/ddsavereader/spreadsheets/SpreadsheetsService.java +++ b/src/main/java/de/robojumper/ddsavereader/spreadsheets/SpreadsheetsService.java @@ -31,7 +31,7 @@ import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.json.gson.GsonFactory; import com.google.api.client.util.store.FileDataStoreFactory; import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.SheetsScopes; @@ -40,7 +40,7 @@ import de.robojumper.ddsavereader.model.CampaignLog.BaseRTTI; import de.robojumper.ddsavereader.model.CampaignLog.Chapter; import de.robojumper.ddsavereader.util.Helpers; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import de.robojumper.ddsavereader.file.DsonTypes; import de.robojumper.ddsavereader.model.Hero; import de.robojumper.ddsavereader.model.SaveState; @@ -49,7 +49,7 @@ public class SpreadsheetsService { private static final String APPLICATION_NAME = "robojumper-" + BuildConfig.NAME + "/" + BuildConfig.VERSION; - private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); + private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); private static final List SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS); private static final String CLIENT_SECRET_DIR = "/client_secret.json"; diff --git a/src/main/java/de/robojumper/ddsavereader/ui/DataPathsDialog.java b/src/main/java/de/robojumper/ddsavereader/ui/DataPathsDialog.java index e4d379a..a59bad8 100644 --- a/src/main/java/de/robojumper/ddsavereader/ui/DataPathsDialog.java +++ b/src/main/java/de/robojumper/ddsavereader/ui/DataPathsDialog.java @@ -102,7 +102,7 @@ public void changedUpdate(DocumentEvent e) { JButton chooseGamePathButton = new JButton("Browse..."); chooseGamePathButton.addActionListener(e -> { - MainWindow.directoryChooser("", s -> this.gameDir = s); + MainWindow.directoryChooser(state.getGameDir(), s -> this.gameDir = s); gameDataPathBox.setText(this.gameDir); }); gameDataPathPanel.add(chooseGamePathButton); @@ -122,7 +122,7 @@ public void changedUpdate(DocumentEvent e) { JButton chooseWorkshopPathButton = new JButton("Browse..."); chooseWorkshopPathButton.addActionListener(e -> { - MainWindow.directoryChooser("", s -> this.modsDir = s); + MainWindow.directoryChooser(state.getModsDir(), s -> this.modsDir = s); workshopPathBox.setText(this.modsDir); }); workshopPathPanel.add(chooseWorkshopPathButton); diff --git a/src/main/java/de/robojumper/ddsavereader/ui/MainWindow.java b/src/main/java/de/robojumper/ddsavereader/ui/MainWindow.java index 7fcc08e..74ad27f 100644 --- a/src/main/java/de/robojumper/ddsavereader/ui/MainWindow.java +++ b/src/main/java/de/robojumper/ddsavereader/ui/MainWindow.java @@ -51,7 +51,7 @@ import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import de.robojumper.ddsavereader.spreadsheets.SpreadsheetsService; import de.robojumper.ddsavereader.spreadsheets.SpreadsheetsService.SheetUpdater; import de.robojumper.ddsavereader.ui.State.SaveFile; @@ -181,7 +181,7 @@ public void windowClosing(WindowEvent e) { mntmSpreadsheets = new JMenuItem("Spreadsheets"); mntmSpreadsheets.setEnabled(false); mntmSpreadsheets.addActionListener(e -> { - if (state.getSaveStatus() == Status.OK) { + if (state.getSaveStatus() != Status.ERROR) { SheetUpdater sheetUpdater; try { final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport(); @@ -323,7 +323,7 @@ public void run() { JButton chooseSavePathButton = new JButton("Browse..."); chooseSavePathButton.addActionListener(e -> { if (confirmLoseChanges()) { - directoryChooser("", s -> state.setSaveDir(s)); + directoryChooser(state.getSaveDir(), s -> state.setSaveDir(s)); updateSaveDir(); updateFiles(); } @@ -332,7 +332,7 @@ public void run() { makeBackupButton = new JButton("Make Backup..."); makeBackupButton.setEnabled(false); makeBackupButton.addActionListener(e -> { - if (state.getSaveStatus() == Status.OK) { + if (state.getSaveStatus() != Status.ERROR) { String result = JOptionPane.showInputDialog(frame, "Choose backup name", new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date())); if (result == null) { @@ -359,7 +359,7 @@ public void run() { restoreBackupButton = new JButton("Load Backup..."); restoreBackupButton.addActionListener(e -> { - if (state.getSaveStatus() == Status.OK && state.hasAnyBackups() && confirmLoseChanges()) { + if (state.getSaveStatus() != Status.ERROR && state.hasAnyBackups() && confirmLoseChanges()) { String[] backups = state.getBackupNames().toArray(new String[0]); Object result = JOptionPane.showInputDialog(frame, "Choose backup", "Restore", JOptionPane.OK_CANCEL_OPTION, null, backups, backups[0]); @@ -438,6 +438,7 @@ public void run() { protected static final void directoryChooser(String def, Consumer onSuccess) { JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(def)); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); chooser.putClientProperty("JFileChooser.appBundleIsTraversable", "always"); int result = chooser.showOpenDialog(null); @@ -449,13 +450,13 @@ protected static final void directoryChooser(String def, Consumer onSucc private void updateSaveDir() { savePathBox.setText(state.getSaveDir()); saveFileStatus.setIcon(state.getSaveStatus().icon); - reloadButton.setEnabled(state.getSaveStatus() == Status.OK); - mntmSpreadsheets.setEnabled(state.getSaveStatus() == Status.OK); + reloadButton.setEnabled(state.getSaveStatus() != Status.ERROR); + mntmSpreadsheets.setEnabled(state.getSaveStatus() != Status.ERROR); updateBackupButtons(); } private void updateBackupButtons() { - makeBackupButton.setEnabled(state.getSaveStatus() == Status.OK); + makeBackupButton.setEnabled(state.getSaveStatus() != Status.ERROR); restoreBackupButton.setEnabled(state.hasAnyBackups()); } diff --git a/src/main/java/de/robojumper/ddsavereader/ui/State.java b/src/main/java/de/robojumper/ddsavereader/ui/State.java index a7c65c6..ee6d921 100644 --- a/src/main/java/de/robojumper/ddsavereader/ui/State.java +++ b/src/main/java/de/robojumper/ddsavereader/ui/State.java @@ -32,7 +32,7 @@ import javax.swing.Icon; import javax.swing.SwingWorker; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import de.robojumper.ddsavereader.file.DsonFile; import de.robojumper.ddsavereader.file.DsonFile.UnhashBehavior; import de.robojumper.ddsavereader.file.DsonWriter; @@ -158,10 +158,10 @@ public String getLastSheetID() { public void setSaveDir(String dir) { if (!Objects.equals(dir, saveDir)) { this.saveDir = dir; - if (new File(saveDir).exists() && saveDir.matches(".*profile_[0-9]*/?")) { + if (new File(saveDir).exists()) { profileString = Paths.get(saveDir).toFile().getName(); new File(BACKUP_DIR, profileString).mkdirs(); - saveStatus = Status.OK; + saveStatus = saveDir.matches(".*profile_[0-9]*/?") ? Status.OK : Status.WARNING; loadFiles(); } else { saveStatus = Status.ERROR; diff --git a/src/main/java/de/robojumper/ddsavereader/updatechecker/UpdateChecker.java b/src/main/java/de/robojumper/ddsavereader/updatechecker/UpdateChecker.java index b59211f..3fc6d81 100644 --- a/src/main/java/de/robojumper/ddsavereader/updatechecker/UpdateChecker.java +++ b/src/main/java/de/robojumper/ddsavereader/updatechecker/UpdateChecker.java @@ -13,7 +13,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParser; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import de.robojumper.ddsavereader.util.Helpers; public class UpdateChecker { @@ -48,8 +48,7 @@ public static Release getLatestRelease() throws IOException { Files.write(CACHED_LAST_RELEASE.toPath(), content.getBytes(StandardCharsets.UTF_8)); } - JsonParser parser = new JsonParser(); - JsonElement rootObject = parser.parse(content); + JsonElement rootObject = JsonParser.parseString(content); return new Release(rootObject.getAsJsonObject().get("tag_name").getAsString(), rootObject.getAsJsonObject().get("html_url").getAsString()); diff --git a/src/main/java/de/robojumper/ddsavereader/util/Helpers.java b/src/main/java/de/robojumper/ddsavereader/util/Helpers.java index 773c259..5599d4a 100644 --- a/src/main/java/de/robojumper/ddsavereader/util/Helpers.java +++ b/src/main/java/de/robojumper/ddsavereader/util/Helpers.java @@ -1,6 +1,6 @@ package de.robojumper.ddsavereader.util; -import de.fuerstenau.buildconfig.BuildConfig; +import de.robojumper.ddsavereader.BuildConfig; import java.io.File; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/de/robojumper/ddsavereader/util/ReadNames.java b/src/main/java/de/robojumper/ddsavereader/util/ReadNames.java index be8d7f4..90f579e 100644 --- a/src/main/java/de/robojumper/ddsavereader/util/ReadNames.java +++ b/src/main/java/de/robojumper/ddsavereader/util/ReadNames.java @@ -68,8 +68,7 @@ public void parseFile(Path filePath, byte[] file, Set names) { @Override public void parseFile(Path filePath, byte[] file, Set names) { String JsonString = new String(file); - JsonParser parser = new JsonParser(); - JsonObject rootObject = parser.parse(JsonString).getAsJsonObject(); + JsonObject rootObject = JsonParser.parseString(JsonString).getAsJsonObject(); JsonArray arrArray = rootObject.getAsJsonArray("skills"); if (arrArray != null) { for (int i = 0; i < arrArray.size(); i++) { @@ -116,9 +115,8 @@ public void parseFile(Path filePath, byte[] file, Set names) { @Override public void parseFile(Path filePath, byte[] file, Set names) { addBaseName(filePath, names); - String JsonString = new String(file); - JsonParser parser = new JsonParser(); - JsonObject rootObject = parser.parse(JsonString).getAsJsonObject(); + String jsonString = new String(file); + JsonObject rootObject = JsonParser.parseString(jsonString).getAsJsonObject(); JsonObject dataObject = rootObject.getAsJsonObject("data"); if (dataObject != null) { JsonArray activitiesArray = dataObject.getAsJsonArray("activities"); @@ -312,9 +310,8 @@ static void addBaseName(Path filePath, Set Set) { // assuming a JSON file where the root object has an array of objects each with a string variable // add that ID string static void addSimpleJSONArrayEntryIDs(byte[] data, String arrayName, String idString, Set Set) { - String JsonString = new String(data); - JsonParser parser = new JsonParser(); - JsonObject rootObject = parser.parse(JsonString).getAsJsonObject(); + String jsonString = new String(data); + JsonObject rootObject = JsonParser.parseString(jsonString).getAsJsonObject(); JsonArray arrArray = rootObject.getAsJsonArray(arrayName); if (arrArray != null) { for (int i = 0; i < arrArray.size(); i++) { diff --git a/src/test/java/de/robojumper/ddsavereader/file/ConverterTests.java b/src/test/java/de/robojumper/ddsavereader/file/ConverterTests.java index 40a5007..27bf6ee 100644 --- a/src/test/java/de/robojumper/ddsavereader/file/ConverterTests.java +++ b/src/test/java/de/robojumper/ddsavereader/file/ConverterTests.java @@ -126,6 +126,7 @@ public void testOtherFiles() throws ParseException, IOException { testCorrectConversion("quirk_monster_class_ids"); testCorrectConversion("dead_hero_entries"); testCorrectConversion("networkFiles"); + testCorrectConversion("valid_additional_mash_entry_indexes"); } private List getResourceFiles(String path) throws IOException { diff --git a/src/test/resources/valid_additional_mash_entry_indexes/persist.raid.json b/src/test/resources/valid_additional_mash_entry_indexes/persist.raid.json new file mode 100644 index 0000000000000000000000000000000000000000..fd9351dd205cd3baea3873748a4a854700a2441e GIT binary patch literal 39652 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