From 3edbcf33fe6f79827e9dbab2baab81e778fc4bdb Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 8 Feb 2025 03:40:15 +0300 Subject: [PATCH 01/57] Kotlinization! * Added `kotlin` library * Converted gradle files --- app/build.gradle | 53 ----------------------- app/build.gradle.kts | 60 ++++++++++++++++++++++++++ build.gradle => build.gradle.kts | 3 +- gradle.properties | 2 + gradle/libs.versions.toml | 12 ++++-- libalpha/build.gradle | 55 ----------------------- libalpha/build.gradle.kts | 60 ++++++++++++++++++++++++++ libbravo/build.gradle | 54 ----------------------- libbravo/build.gradle.kts | 59 +++++++++++++++++++++++++ libcharlie/build.gradle | 54 ----------------------- libcharlie/build.gradle.kts | 59 +++++++++++++++++++++++++ settings.gradle => settings.gradle.kts | 8 ++-- 12 files changed, 254 insertions(+), 225 deletions(-) delete mode 100644 app/build.gradle create mode 100644 app/build.gradle.kts rename build.gradle => build.gradle.kts (62%) delete mode 100644 libalpha/build.gradle create mode 100644 libalpha/build.gradle.kts delete mode 100644 libbravo/build.gradle create mode 100644 libbravo/build.gradle.kts delete mode 100644 libcharlie/build.gradle create mode 100644 libcharlie/build.gradle.kts rename settings.gradle => settings.gradle.kts (86%) diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index e995dbb..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -plugins { - alias(libs.plugins.android.application) -} - -android { - namespace 'org.qp.android.questopiabundle' - compileSdk 35 - - defaultConfig { - applicationId "org.qp.android.questopiabundle" - minSdk 26 - targetSdk 34 - versionCode 100000 - versionName "0.0.1" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - - buildFeatures { - viewBinding true - buildConfig true - aidl true - } - -} - -dependencies { - implementation project(':libalpha') - implementation project(':libbravo') - implementation project(':libcharlie') - - implementation libs.appcompat - implementation libs.material - implementation libs.constraintlayout - - implementation libs.storage - implementation libs.jsoup - - testImplementation libs.junit - androidTestImplementation libs.ext.junit - androidTestImplementation libs.espresso.core -} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..c124b99 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "org.qp.android.questopiabundle" + compileSdk = 35 + + defaultConfig { + applicationId = "org.qp.android.questopiabundle" + minSdk = 26 + targetSdk = 34 + versionCode = 100000 + versionName = "0.0.1" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + } + + buildFeatures { + viewBinding = true + buildConfig = true + aidl = true + } + +} + +dependencies { + implementation(project(":libalpha")) + implementation(project(":libbravo")) + implementation(project(":libcharlie")) + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.constraintlayout) + + implementation(libs.storage) + implementation(libs.jsoup) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} \ No newline at end of file diff --git a/build.gradle b/build.gradle.kts similarity index 62% rename from build.gradle rename to build.gradle.kts index 41b070a..bcd3ab9 100644 --- a/build.gradle +++ b/build.gradle.kts @@ -1,5 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { alias(libs.plugins.android.application) apply false - alias(libs.plugins.android.library) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.android) apply false } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 4387edc..20e2a01 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,8 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cc700e1..cb2339e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,7 @@ [versions] agp = "8.7.3" +kotlin = "2.0.21" +coreKtx = "1.15.0" junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" @@ -10,15 +12,17 @@ storage = "1.5.5" jsoup = "1.18.1" [libraries] +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" } -ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } -espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } -appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } -constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } storage = { module = "com.anggrayudi:storage", version.ref = "storage" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } diff --git a/libalpha/build.gradle b/libalpha/build.gradle deleted file mode 100644 index 5ac0126..0000000 --- a/libalpha/build.gradle +++ /dev/null @@ -1,55 +0,0 @@ -plugins { - alias(libs.plugins.android.library) -} - -android { - namespace 'org.qp.android.questopiabundle.libalpha' - compileSdk 35 - - defaultConfig { - minSdk 26 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - externalNativeBuild { - cmake { - arguments "-DBUILD_JAVA=1" - cFlags "-DANDROID" - } - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - - externalNativeBuild { - cmake { - path "src/main/cpp/CMakeLists.txt" - version "3.22.1" - } - } - - ndkVersion = '27.2.12479018' - lint { - abortOnError false - } - -} - -dependencies { - implementation libs.appcompat - implementation libs.material - - testImplementation libs.junit - androidTestImplementation libs.ext.junit - androidTestImplementation libs.espresso.core -} \ No newline at end of file diff --git a/libalpha/build.gradle.kts b/libalpha/build.gradle.kts new file mode 100644 index 0000000..eccdfa7 --- /dev/null +++ b/libalpha/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "org.qp.android.questopiabundle.libalpha" + compileSdk = 35 + + defaultConfig { + minSdk = 26 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + externalNativeBuild { + cmake { + arguments += listOf("-DBUILD_JAVA=1") + cFlags += listOf("-DANDROID") + } + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + } + + externalNativeBuild { + cmake { + path = File("src/main/cpp/CMakeLists.txt") + version = "3.22.1" + } + } + + ndkVersion = "27.2.12479018" + lint { + abortOnError = false + } + +} + +dependencies { + implementation (libs.androidx.appcompat) + implementation (libs.material) + + testImplementation (libs.junit) + androidTestImplementation (libs.androidx.junit) + androidTestImplementation (libs.androidx.espresso.core) +} \ No newline at end of file diff --git a/libbravo/build.gradle b/libbravo/build.gradle deleted file mode 100644 index 6bfdaa3..0000000 --- a/libbravo/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -plugins { - alias(libs.plugins.android.library) -} - -android { - namespace 'org.qp.android.questopiabundle.libbravo' - compileSdk 35 - - defaultConfig { - minSdk 26 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - externalNativeBuild { - cmake { - cFlags "-D_ANDROID" - } - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - - externalNativeBuild { - cmake { - path "src/main/jni/CMakeLists.txt" - version "3.22.1" - } - } - - ndkVersion = '27.2.12479018' - lint { - abortOnError false - } - -} - -dependencies { - implementation libs.appcompat - implementation libs.material - - testImplementation libs.junit - androidTestImplementation libs.ext.junit - androidTestImplementation libs.espresso.core -} \ No newline at end of file diff --git a/libbravo/build.gradle.kts b/libbravo/build.gradle.kts new file mode 100644 index 0000000..e7672b0 --- /dev/null +++ b/libbravo/build.gradle.kts @@ -0,0 +1,59 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "org.qp.android.questopiabundle.libbravo" + compileSdk = 35 + + defaultConfig { + minSdk = 26 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + externalNativeBuild { + cmake { + cFlags += listOf("-DANDROID") + } + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + } + + externalNativeBuild { + cmake { + path = File("src/main/jni/CMakeLists.txt") + version = "3.22.1" + } + } + + ndkVersion = "27.2.12479018" + lint { + abortOnError = false + } + +} + +dependencies { + implementation (libs.androidx.appcompat) + implementation (libs.material) + + testImplementation (libs.junit) + androidTestImplementation (libs.androidx.junit) + androidTestImplementation (libs.androidx.espresso.core) +} \ No newline at end of file diff --git a/libcharlie/build.gradle b/libcharlie/build.gradle deleted file mode 100644 index 5c7ebaa..0000000 --- a/libcharlie/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -plugins { - alias(libs.plugins.android.library) -} - -android { - namespace 'org.qp.android.questopiabundle.libcharlie' - compileSdk 35 - - defaultConfig { - minSdk 26 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - externalNativeBuild { - cmake { - cFlags "-D_ANDROID" - } - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - - externalNativeBuild { - cmake { - path "src/main/cpp/CMakeLists.txt" - version "3.22.1" - } - } - - ndkVersion = '27.2.12479018' - lint { - abortOnError false - } - -} - -dependencies { - implementation libs.appcompat - implementation libs.material - - testImplementation libs.junit - androidTestImplementation libs.ext.junit - androidTestImplementation libs.espresso.core -} \ No newline at end of file diff --git a/libcharlie/build.gradle.kts b/libcharlie/build.gradle.kts new file mode 100644 index 0000000..d72274d --- /dev/null +++ b/libcharlie/build.gradle.kts @@ -0,0 +1,59 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "org.qp.android.questopiabundle.libcharlie" + compileSdk = 35 + + defaultConfig { + minSdk = 26 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + externalNativeBuild { + cmake { + cFlags += listOf("-D_ANDROID") + } + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + } + + externalNativeBuild { + cmake { + path = File("src/main/cpp/CMakeLists.txt") + version = "3.22.1" + } + } + + ndkVersion = "27.2.12479018" + lint { + abortOnError = false + } + +} + +dependencies { + implementation (libs.androidx.appcompat) + implementation (libs.material) + + testImplementation (libs.junit) + androidTestImplementation (libs.androidx.junit) + androidTestImplementation (libs.androidx.espresso.core) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle.kts similarity index 86% rename from settings.gradle rename to settings.gradle.kts index d9f2697..8b522cb 100644 --- a/settings.gradle +++ b/settings.gradle.kts @@ -20,7 +20,7 @@ dependencyResolutionManagement { } rootProject.name = "Questopia Bundle" -include ':app' -include ':libalpha' -include ':libbravo' -include ':libcharlie' +include(":app") +include(":libalpha") +include(":libbravo") +include(":libcharlie:") \ No newline at end of file From ca9369231952e48f1d766413ef9f3c6ea27836d9 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:56:02 +0300 Subject: [PATCH 02/57] Changed the formatting of the code --- libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java index 03976a9..21c6f78 100644 --- a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java +++ b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java @@ -2,13 +2,13 @@ public abstract class SNXLib { - public record ListItem(String image , String text) { } + public record ListItem(String image, String text) { } - public record VarValResp(boolean isSuccess , String stringValue , int intValue) { } + public record VarValResp(boolean isSuccess, String stringValue, int intValue) { } public record ExecutionState(String loc, int actIndex, int lineNum) { } - public record ErrorData(String locName , int errorNum , int index , int line) { } + public record ErrorData(String locName, int errorNum, int index, int line) { } static { System.loadLibrary("snxqsp"); From bf2a9c894419eb0ae64fa97ca42d91b5c462dce0 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:57:25 +0300 Subject: [PATCH 03/57] Changed the name of the threads --- .../qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java | 2 +- .../android/questopiabundle/lib/impl/LibCharlieProxyImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java index c873eef..196846a 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java @@ -244,7 +244,7 @@ public void startLibThread() { Thread.currentThread().interrupt(); } } - }, "libQSP"); + }, "libNDKQSP"); libThread.start(); } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java index 812de8c..d7718df 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java @@ -233,7 +233,7 @@ public void startLibThread() { Thread.currentThread().interrupt(); } } - }, "libQSP"); + }, "libSNXQSP"); libThread.start(); } From a13bcb0d97a6c56fd2657589737317dd9baa4c48 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:59:02 +0300 Subject: [PATCH 04/57] Changed the warning text --- .../qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java | 2 +- .../qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java | 2 +- .../android/questopiabundle/lib/impl/LibCharlieProxyImpl.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java index 4cb4c85..dee4f13 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java @@ -244,7 +244,7 @@ public void stopLibThread() { } libThreadInit = false; } else { - Log.w(TAG, "libqsp thread has been started, but not initialized"); + Log.w(TAG, "lib thread has been started, but not initialized"); } libThread.interrupt(); } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java index 196846a..d35927d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java @@ -258,7 +258,7 @@ public void stopLibThread() { } libThreadInit = false; } else { - Log.w(TAG, "libqsp thread has been started, but not initialized"); + Log.w(TAG, "lib thread has been started, but not initialized"); } libThread.interrupt(); } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java index d7718df..5c0d099 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java @@ -247,7 +247,7 @@ public void stopLibThread() { } libThreadInit = false; } else { - Log.w(TAG, "libqsp thread has been started, but not initialized"); + Log.w(TAG, "lib thread has been started, but not initialized"); } libThread.interrupt(); } From c5c432e67ed1827aef66d7637a2e93ce4e8d6dc1 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 8 Feb 2025 23:00:13 +0300 Subject: [PATCH 05/57] Replaced the `throwIfNotMainThread` method with a synchronization method --- .../questopiabundle/lib/impl/LibAlphaProxyImpl.java | 8 ++++---- .../questopiabundle/lib/impl/LibBravoProxyImpl.java | 6 ++---- .../questopiabundle/lib/impl/LibCharlieProxyImpl.java | 6 ++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java index dee4f13..cdd4924 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java @@ -64,8 +64,7 @@ private DocumentFile getCurGameDir() { return DocumentFileCompat.fromUri(context, gameState.gameDirUri); } - private void runOnQspThread(final Runnable runnable) { -// throwIfNotMainThread(); + private synchronized void runOnQspThread(final Runnable runnable) { if (libThread == null) { Log.w(TAG, "Lib thread has not been started!"); return; @@ -165,6 +164,7 @@ private boolean loadInterfaceConfiguration() { private ArrayList getActionsList() { var actions = new ArrayList(); var curGameDir = getCurGameDir(); + for (var element : getActions()) { var newElement = new LibListItem(element); if (isNotEmptyOrBlank(newElement.pathToImage)) { @@ -181,6 +181,7 @@ private ArrayList getActionsList() { : newElement.text; actions.add(newElement); } + return actions; } @@ -234,8 +235,7 @@ public void startLibThread() { libThread.start(); } - public void stopLibThread() { -// throwIfNotMainThread(); + public synchronized void stopLibThread() { if (libThread == null) return; if (libThreadInit) { var handler = libHandler; diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java index d35927d..7751986 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java @@ -66,8 +66,7 @@ private DocumentFile getCurGameDir() { return DocumentFileCompat.fromUri(context, gameState.gameDirUri); } - private void runOnQspThread(final Runnable runnable) { -// throwIfNotMainThread(); + private synchronized void runOnQspThread(final Runnable runnable) { if (libThread == null) { Log.w(TAG, "Lib thread has not been started!"); return; @@ -248,8 +247,7 @@ public void startLibThread() { libThread.start(); } - public void stopLibThread() { -// throwIfNotMainThread(); + public synchronized void stopLibThread() { if (libThread == null) return; if (libThreadInit) { var handler = libHandler; diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java index 5c0d099..88dc56e 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java @@ -65,8 +65,7 @@ private DocumentFile getCurGameDir() { return DocumentFileCompat.fromUri(context, gameState.gameDirUri); } - private void runOnQspThread(final Runnable runnable) { -// throwIfNotMainThread(); + private synchronized void runOnQspThread(final Runnable runnable) { if (libThread == null) { Log.w(TAG, "Lib thread has not been started!"); return; @@ -237,8 +236,7 @@ public void startLibThread() { libThread.start(); } - public void stopLibThread() { -// throwIfNotMainThread(); + public synchronized void stopLibThread() { if (libThread == null) return; if (libThreadInit) { var handler = libHandler; From 3a7c771cd19c216c6dd444bfdf7dbc7a3d2e3cec Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 8 Feb 2025 23:02:08 +0300 Subject: [PATCH 06/57] Removed unused methods in the `utils` package --- .../questopiabundle/utils/StringUtil.java | 4 -- .../questopiabundle/utils/ThreadUtil.java | 38 ------------------- 2 files changed, 42 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java index acf3854..3ae0d39 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java @@ -10,10 +10,6 @@ public static boolean isNotEmptyOrBlank(String str) { return str != null && !str.isEmpty() && !str.isBlank(); } - public static boolean isNotEmpty(String str) { - return str != null && !str.isEmpty(); - } - public static boolean isNullOrEmpty(String str) { return str == null || str.isEmpty(); } diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java index bed23e1..03741c7 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java @@ -1,8 +1,5 @@ package org.qp.android.questopiabundle.utils; -import android.os.Handler; -import android.os.Looper; - public final class ThreadUtil { /** @@ -12,39 +9,4 @@ public static boolean isSameThread(Thread thread) { return Thread.currentThread().equals(thread); } - /** - * Throw IllegalStateException, if the method is called in the main thread - */ - public static void assertNonUiThread() { - if (Looper.getMainLooper().getThread() == Thread.currentThread()) { - throw new IllegalStateException("This should not be run on the UI thread"); - } - } - - /** - * @return true if the current thread is the main one, otherwise false - */ - public static boolean isMainThread() { - // TODO: 08.12.2024 Refactor this. The service doesn't have a main thread! - return Thread.currentThread().equals(Looper.getMainLooper().getThread()); - } - - /** - * Throws a RuntimeException if the method is not called from the main thread. - */ - public static void throwIfNotMainThread() { - if (!isMainThread()) { - throw new RuntimeException("Must be called from the main thread"); - } - } - - public static void runOnUiThread(Runnable action) { - if (!isMainThread()) { - var mHandler = new Handler(Looper.getMainLooper()); - mHandler.post(action); - } else { - action.run(); - } - } - } From 5aafa1ba3f4b7f0674b020657c27d06d5a17c301 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Feb 2025 03:45:11 +0300 Subject: [PATCH 07/57] Fixed build `LibBravo` --- libbravo/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbravo/build.gradle.kts b/libbravo/build.gradle.kts index e7672b0..b4a6011 100644 --- a/libbravo/build.gradle.kts +++ b/libbravo/build.gradle.kts @@ -14,7 +14,7 @@ android { consumerProguardFiles("consumer-rules.pro") externalNativeBuild { cmake { - cFlags += listOf("-DANDROID") + cFlags += listOf("-D_ANDROID") } } } From 8d0c7cecdd5d56f40abb76562ae5f91b5d913b34 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Feb 2025 03:46:04 +0300 Subject: [PATCH 08/57] Rename .java to .kt --- .../android/questopiabundle/utils/{FileUtil.java => FileUtil.kt} | 0 .../android/questopiabundle/utils/{HtmlUtil.java => HtmlUtil.kt} | 0 .../android/questopiabundle/utils/{PathUtil.java => PathUtil.kt} | 0 .../questopiabundle/utils/{StreamUtil.java => StreamUtil.kt} | 0 .../questopiabundle/utils/{StringUtil.java => StringUtil.kt} | 0 .../questopiabundle/utils/{ThreadUtil.java => ThreadUtil.kt} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/org/qp/android/questopiabundle/utils/{FileUtil.java => FileUtil.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/utils/{HtmlUtil.java => HtmlUtil.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/utils/{PathUtil.java => PathUtil.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/utils/{StreamUtil.java => StreamUtil.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/utils/{StringUtil.java => StringUtil.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/utils/{ThreadUtil.java => ThreadUtil.kt} (100%) diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.java rename to app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.java rename to app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.java rename to app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.java rename to app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java rename to app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java rename to app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt From d4dcce06590eea0fdc28c01ee76d0544b5afde14 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Feb 2025 03:46:05 +0300 Subject: [PATCH 09/57] Convert to Kotlin --- .../lib/impl/LibAlphaProxyImpl.java | 4 +- .../lib/impl/LibBravoProxyImpl.java | 22 +-- .../lib/impl/LibCharlieProxyImpl.java | 2 +- .../android/questopiabundle/utils/FileUtil.kt | 166 +++++++++--------- .../android/questopiabundle/utils/HtmlUtil.kt | 60 +++---- .../android/questopiabundle/utils/PathUtil.kt | 25 ++- .../questopiabundle/utils/StreamUtil.kt | 27 +-- .../questopiabundle/utils/StringUtil.kt | 24 +-- .../questopiabundle/utils/ThreadUtil.kt | 12 +- 9 files changed, 169 insertions(+), 173 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java index cdd4924..bc294aa 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java @@ -500,7 +500,7 @@ public void onOpenGameStatus(String file) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); } else { try { - var saveFile = fromFullPath(context, file, getCurGameDir()); + var saveFile = fromFullPath(context, file); if (!isWritableFile(context, saveFile)) { if (gameInterface != null) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found"); @@ -588,7 +588,7 @@ public void onShowWindow(int type, boolean toShow) { @Override public void onOpenGame(String file, boolean isNewGame) { - var newGameDir = fromFullPath(context, file, getCurGameDir()); + var newGameDir = fromFullPath(context, file); if (newGameDir == null || !newGameDir.exists()) { Log.e(TAG, "Game directory not found: " + file); return; diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java index 7751986..a6d3f1d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java @@ -90,6 +90,7 @@ private synchronized void runOnQspThread(final Runnable runnable) { private boolean loadGameWorld() { var gameFileUri = gameState.gameFileUri; var gameFile = DocumentFileCompat.fromUri(context, gameState.gameFileUri); + if (gameFile == null) return false; var gameFileFullPath = documentWrap(gameFile).getAbsolutePath(context); var gameData = getFileContents(context, gameFileUri); if (gameData == null) return false; @@ -512,17 +513,15 @@ public void OpenGame(String filename) { if (gameInterface == null) return; gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); } else { + if (gameInterface == null) return; try { - var saveFile = fromFullPath(context, filename, getCurGameDir()); - if (!isWritableFile(context, saveFile)) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found"); - } - Log.e(TAG, "Save file not found"); - return; - } - if (gameInterface != null) { + var saveFile = fromFullPath(context, filename); + if (isWritableFile(context, saveFile)) { + if (saveFile == null) return; gameInterface.doWithCounterDisabled(() -> loadGameState(saveFile.getUri())); + } else { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found"); + Log.e(TAG, "Save file not found"); } } catch (Exception e) { if (gameInterface != null) { @@ -542,6 +541,7 @@ public void SaveGame(String filename) { var save = new File(filename); var saveFile = findOrCreateFile(context, getCurGameDir(), save.getName(), MimeType.TEXT); if (isWritableFile(context, saveFile)) { + if (saveFile == null) return; saveGameState(saveFile.getUri()); } else { if (gameInterface != null) { @@ -613,7 +613,7 @@ public void ShowWindow(int type, boolean isShow) { @Override public byte[] GetFileContents(String path) { - var targetFile = fromFullPath(context, path, getCurGameDir()); + var targetFile = fromFullPath(context, path); if (targetFile == null) return null; var targetFileUri = targetFile.getUri(); return getFileContents(context , targetFileUri); @@ -621,7 +621,7 @@ public byte[] GetFileContents(String path) { @Override public void ChangeQuestPath(String path) { - var newGameDir = fromFullPath(context, path , getCurGameDir()); + var newGameDir = fromFullPath(context, path); if (newGameDir == null || !newGameDir.exists()) { Log.e(TAG,"Game directory not found: " + path); return; diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java index 88dc56e..af40ffa 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java @@ -502,7 +502,7 @@ public void OpenGame(String filename) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); } else { try { - var saveFile = fromFullPath(context, filename, getCurGameDir()); + var saveFile = fromFullPath(context, filename); if (!isWritableFile(context, saveFile)) { if (gameInterface != null) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found"); diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt index 01969d0..86502e6 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt @@ -1,108 +1,104 @@ -package org.qp.android.questopiabundle.utils; +package org.qp.android.questopiabundle.utils -import android.content.Context; -import android.net.Uri; -import android.util.Log; +import android.content.Context +import android.net.Uri +import android.util.Log +import androidx.annotation.NonNull +import androidx.documentfile.provider.DocumentFile +import com.anggrayudi.storage.FileWrapper +import com.anggrayudi.storage.file.CreateMode +import com.anggrayudi.storage.file.DocumentFileCompat +import com.anggrayudi.storage.file.child +import com.anggrayudi.storage.file.isWritable +import com.anggrayudi.storage.file.makeFile +import org.qp.android.questopiabundle.utils.StreamUtil.copy +import java.io.ByteArrayOutputStream +import java.io.IOException -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.documentfile.provider.DocumentFile; +object FileUtil { -import com.anggrayudi.storage.FileWrapper; -import com.anggrayudi.storage.file.CreateMode; -import com.anggrayudi.storage.file.DocumentFileUtils; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -public final class FileUtil { - - public static boolean isWritableFile(Context context, DocumentFile file) { - if (file == null) return false; - var canWrite = DocumentFileUtils.isWritable(file, context); - return file.exists() && file.isFile() && canWrite; + @JvmStatic + fun isWritableFile(context: Context, file: DocumentFile?): Boolean { + if (file == null) return false + val canWrite = file.isWritable(context) + return file.exists() && file.isFile && canWrite } - @Nullable - public static byte[] getFileContents(@NonNull Context context, - @NonNull Uri uriContent) { - var resolver = context.getContentResolver(); - try (var in = resolver.openInputStream(uriContent); - var out = new ByteArrayOutputStream()) { - if (in != null) { - StreamUtil.copy(in, out); - } else { - throw new NullPointerException(); + @JvmStatic + fun getFileContents( + context: Context, + uriContent: Uri + ): ByteArray? { + val resolver = context.contentResolver + try { + resolver.openInputStream(uriContent).use { `in` -> + ByteArrayOutputStream().use { out -> + if (`in` != null) { + copy(`in`, out) + } else { + throw NullPointerException() + } + return out.toByteArray() + } } - return out.toByteArray(); - } catch (Exception ex) { + } catch (ex: Exception) { // TODO: 04.12.2024 Add logger - Log.e("FileUtil", "Error reading file: " + uriContent, ex); - return null; + Log.e("FileUtil", "Error reading file: $uriContent", ex) + return null } } - public static FileWrapper.Document documentWrap(DocumentFile inputFile) { - return new FileWrapper.Document(inputFile); + @JvmStatic + fun documentWrap(inputFile: DocumentFile): FileWrapper.Document { + return FileWrapper.Document(inputFile) } - public static void writeFileContents(@NonNull Context context, - @NonNull Uri uriContent, - byte[] dataToWrite) { - var resolver = context.getContentResolver(); - try (var out = resolver.openOutputStream(uriContent, "w")) { - if (out != null) { - out.write(dataToWrite); - } else { - throw new IOException("Input is NULL!"); + @JvmStatic + fun writeFileContents( + context: Context, + uriContent: Uri, + dataToWrite: ByteArray? + ) { + val resolver = context.contentResolver + try { + resolver.openOutputStream(uriContent, "w").use { out -> + if (out != null) { + out.write(dataToWrite) + } else { + throw IOException("Input is NULL!") + } } - } catch (IOException ex) { + } catch (ex: IOException) { // TODO: 04.12.2024 Add logger - Log.e("FileUtil", "Error reading file: " + uriContent, ex); + Log.e("FileUtil", "Error reading file: $uriContent", ex) } } - @Nullable - public static DocumentFile findOrCreateFile(Context context, - DocumentFile srcDir, - String name, - String mimeType) { - return DocumentFileUtils.makeFile(srcDir, context, name, mimeType, CreateMode.REUSE); + @JvmStatic + fun findOrCreateFile( + context: Context, + srcDir: DocumentFile, + name: String, + mimeType: String? + ): DocumentFile? { + return srcDir.makeFile(context, name, mimeType, CreateMode.REUSE) } - public static DocumentFile fromRelPath(@NonNull Context context, - @NonNull final String path, - @NonNull DocumentFile parentDir, - final boolean requiresWriteAccess) { - return DocumentFileUtils.child(parentDir, context, path, requiresWriteAccess); + @JvmStatic + fun fromRelPath( + context: Context, + path: String, + parentDir: DocumentFile, + requiresWriteAccess: Boolean + ): DocumentFile? { + return parentDir.child(context, path, requiresWriteAccess) } - @Nullable - public static DocumentFile fromFullPath(@NonNull Context context, - @NonNull String fullPath, - @NonNull DocumentFile rootDir) { - var findDir = rootDir; - var nameGameDir = rootDir.getName(); - - try { - var index = fullPath.indexOf(nameGameDir); - var subString = fullPath.substring(index); - var splitString = subString.replace(nameGameDir + "/", ""); - var pathToFileSegments = splitString.split("/"); - - for (var segment : pathToFileSegments) { - if (segment.isEmpty()) { - continue; - } - findDir = fromRelPath(context, segment, findDir, true); - if (findDir == null) { - break; - } - } - } catch (NullPointerException i) { - return null; - } - - return findDir; + @JvmStatic + fun fromFullPath( + context: Context, + fullPath: String + ): DocumentFile? { + return DocumentFileCompat.fromFullPath(context, fullPath, requiresWriteAccess = true) } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt index 91ab5cb..3adb22f 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt @@ -1,51 +1,51 @@ -package org.qp.android.questopiabundle.utils; +package org.qp.android.questopiabundle.utils -import static org.qp.android.questopiabundle.utils.StringUtil.isNullOrEmpty; +import org.jsoup.Jsoup +import org.jsoup.safety.Safelist +import org.qp.android.questopiabundle.utils.StringUtil.isNullOrEmpty +import java.util.regex.Pattern -import org.jsoup.Jsoup; -import org.jsoup.safety.Safelist; +object HtmlUtil { -import java.util.regex.Pattern; + private val HTML_PATTERN: Pattern = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>") -public final class HtmlUtil { - - private static final Pattern HTML_PATTERN = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>"); - - public static boolean isContainsHtmlTags(String text) { - return HTML_PATTERN.matcher(text).find(); + @JvmStatic + fun isContainsHtmlTags(text: String): Boolean { + return HTML_PATTERN.matcher(text).find() } - public static String getSrcDir(String html) { - var document = Jsoup.parse(html); - var imageElement = document.select("img").first(); - if (imageElement == null) return ""; - return imageElement.attr("src"); + @JvmStatic + fun getSrcDir(html: String): String { + val document = Jsoup.parse(html) + val imageElement = document.select("img").first() ?: return "" + return imageElement.attr("src") } /** - * Remove HTML tags from the html string and return the resulting string. + * Remove HTML tags from the `html` string and return the resulting string. */ - public static String removeHtmlTags(String html) { - if (isNullOrEmpty(html)) return ""; + @JvmStatic + fun removeHtmlTags(html: String): String { + if (isNullOrEmpty(html)) return "" - var result = new StringBuilder(); - var len = html.length(); - var fromIdx = 0; + val result = StringBuilder() + val len = html.length + var fromIdx = 0 while (fromIdx < len) { - var idx = html.indexOf('<', fromIdx); + val idx = html.indexOf('<', fromIdx) if (idx == -1) { - result.append(html.substring(fromIdx)); - break; + result.append(html.substring(fromIdx)) + break } - result.append(html, fromIdx, idx); - var endIdx = html.indexOf('>', idx + 1); + result.append(html, fromIdx, idx) + val endIdx = html.indexOf('>', idx + 1) if (endIdx == -1) { - return Jsoup.clean(html, Safelist.none()); + return Jsoup.clean(html, Safelist.none()) } - fromIdx = endIdx + 1; + fromIdx = endIdx + 1 } - return result.toString(); + return result.toString() } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt index 4a6a8f4..e8675ed 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt @@ -1,13 +1,11 @@ -package org.qp.android.questopiabundle.utils; +package org.qp.android.questopiabundle.utils -import androidx.annotation.NonNull; +object PathUtil { -public final class PathUtil { - - @NonNull - public static String getFilename(@NonNull String path) { - var idx = path.lastIndexOf('/'); - return idx == -1 ? path : path.substring(idx + 1); + @JvmStatic + fun getFilename(path: String): String { + val idx = path.lastIndexOf('/') + return if (idx == -1) path else path.substring(idx + 1) } /** @@ -15,12 +13,13 @@ public final class PathUtil { * * @implNote Removes "./" from the beginning of the path, replaces all occurrences of "\" with "/". */ - public static String normalizeContentPath(String path) { - if (path == null) return null; - var result = path; + @JvmStatic + fun normalizeContentPath(path: String?): String { + if (path == null) return "" + var result: String = path if (result.startsWith("./")) { - result = result.substring(2); + result = result.substring(2) } - return result.replace("\\", "/"); + return result.replace("\\", "/") } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt index 069d776..0fcc090 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt @@ -1,22 +1,23 @@ -package org.qp.android.questopiabundle.utils; +package org.qp.android.questopiabundle.utils -import androidx.annotation.NonNull; +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +object StreamUtil { -public final class StreamUtil { - private static final int BUFFER_SIZE = 8192; + private const val BUFFER_SIZE = 8192 /** - * Copies data from the from stream to the to stream. + * Copies data from the `from` stream to the `to` stream. */ - public static void copy(@NonNull InputStream from, OutputStream to) throws IOException { - var buffer = new byte[BUFFER_SIZE]; - int bytesRead; - while ((bytesRead = from.read(buffer)) > 0) { - to.write(buffer, 0, bytesRead); + @JvmStatic + @Throws(IOException::class) + fun copy(from: InputStream, to: OutputStream) { + val buffer = ByteArray(BUFFER_SIZE) + var bytesRead: Int + while ((from.read(buffer).also { bytesRead = it }) > 0) { + to.write(buffer, 0, bytesRead) } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt index 3ae0d39..902f40b 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt @@ -1,22 +1,22 @@ -package org.qp.android.questopiabundle.utils; +package org.qp.android.questopiabundle.utils -import androidx.annotation.NonNull; +import org.jetbrains.annotations.Contract -import org.jetbrains.annotations.Contract; +object StringUtil { -public final class StringUtil { - - public static boolean isNotEmptyOrBlank(String str) { - return str != null && !str.isEmpty() && !str.isBlank(); + @JvmStatic + fun isNotEmptyOrBlank(str: String?): Boolean { + return !str.isNullOrEmpty() && str.isNotBlank() } - public static boolean isNullOrEmpty(String str) { - return str == null || str.isEmpty(); + @JvmStatic + fun isNullOrEmpty(str: String?): Boolean { + return str.isNullOrEmpty() } - @NonNull + @JvmStatic @Contract(value = "!null -> param1", pure = true) - public static String getStringOrEmpty(String str) { - return str != null ? str : ""; + fun getStringOrEmpty(str: String?): String { + return str ?: "" } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt index 03741c7..ce3068f 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt @@ -1,12 +1,12 @@ -package org.qp.android.questopiabundle.utils; +package org.qp.android.questopiabundle.utils -public final class ThreadUtil { +object ThreadUtil { /** - * @return true if the current thread is thread, otherwise false + * @return `true` if the current thread is `thread`, otherwise `false` */ - public static boolean isSameThread(Thread thread) { - return Thread.currentThread().equals(thread); + @JvmStatic + fun isSameThread(thread: Thread): Boolean { + return Thread.currentThread() == thread } - } From 1f98e0d12b4c9a2764c19aa39606aa6cb14b7649 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Feb 2025 03:47:25 +0300 Subject: [PATCH 10/57] Removed unused parameter --- app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 62fbffa..eb6c31c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,7 +16,6 @@ From 5527b8618f6dbafac270819222b63e7eb97f65c7 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Mon, 10 Feb 2025 04:45:59 +0300 Subject: [PATCH 11/57] Renamed one of the binding functions --- .../src/main/cpp/src/bindings/android/android_control.c | 2 +- .../main/cpp/src/bindings/android/org_libsnxqsp_jni_SNXLib.h | 4 ++-- libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libcharlie/src/main/cpp/src/bindings/android/android_control.c b/libcharlie/src/main/cpp/src/bindings/android/android_control.c index ed286cb..e14dd1b 100644 --- a/libcharlie/src/main/cpp/src/bindings/android/android_control.c +++ b/libcharlie/src/main/cpp/src/bindings/android/android_control.c @@ -135,7 +135,7 @@ JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetVarsDesc(JNIEnv *e } /* Possibility to change the text of the additional description */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPIsVarsDescChanged(JNIEnv *env, jobject this) +JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_isVarsDescChanged(JNIEnv *env, jobject this) { return qspIsVarsDescChanged; } diff --git a/libcharlie/src/main/cpp/src/bindings/android/org_libsnxqsp_jni_SNXLib.h b/libcharlie/src/main/cpp/src/bindings/android/org_libsnxqsp_jni_SNXLib.h index da4c43d..10f9716 100644 --- a/libcharlie/src/main/cpp/src/bindings/android/org_libsnxqsp_jni_SNXLib.h +++ b/libcharlie/src/main/cpp/src/bindings/android/org_libsnxqsp_jni_SNXLib.h @@ -121,10 +121,10 @@ JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetVarsDesc /* * Class: org_libsnxqsp_jni_SNXLib - * Method: QSPIsVarsDescChanged + * Method: isVarsDescChanged * Signature: ()Z */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPIsVarsDescChanged +JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_isVarsDescChanged (JNIEnv *, jobject); /* diff --git a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java index 21c6f78..b9c3051 100644 --- a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java +++ b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java @@ -30,7 +30,7 @@ public record ErrorData(String locName, int errorNum, int index, int line) { } public native boolean QSPIsMainDescChanged(); /* Vars desc */ public native String QSPGetVarsDesc(); - public native boolean QSPIsVarsDescChanged(); + public native boolean isVarsDescChanged(); public native int getVarValuesCount(String name); public native Object QSPGetVarValues(String name, int ind);//!!!STUB public native int QSPGetMaxVarsCount(); From 6e8a844d9aaaf552f1cf6d20f0747531aedf670f Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Mon, 10 Feb 2025 04:47:48 +0300 Subject: [PATCH 12/57] Added for some methods in the binding converter to JVM strings --- .../src/main/cpp/src/bindings/android/android_control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcharlie/src/main/cpp/src/bindings/android/android_control.c b/libcharlie/src/main/cpp/src/bindings/android/android_control.c index e14dd1b..8181dfa 100644 --- a/libcharlie/src/main/cpp/src/bindings/android/android_control.c +++ b/libcharlie/src/main/cpp/src/bindings/android/android_control.c @@ -85,13 +85,13 @@ JNIEXPORT jobject JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetCurStateData(JNIEn /* Version */ JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetVersion(JNIEnv *env, jobject this) { - return QSP_VER; + return snxToJavaString(env, QSP_VER); } /* Date and time of compilation */ JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetCompiledDateTime(JNIEnv *env, jobject this) { - return QSP_FMT(__DATE__) QSP_FMT(", ") QSP_FMT(__TIME__); + return snxToJavaString(env, QSP_FMT(__DATE__) QSP_FMT(", ") QSP_FMT(__TIME__)); } /* ------------------------------------------------------------ */ /* Number of full location updates */ From 869af42f5d4431ef6987fd38888cd5d5fc46143c Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Mon, 10 Feb 2025 04:52:47 +0300 Subject: [PATCH 13/57] Refactored `LibBravo` * Changed the package name for the abstract class * Added some functions from the official library * Updated `NDKLib` class with objects from `QSPLib` class * qspToJVMString -> ndkToJavaString * qspFromJavaString -> ndkFromJavaString --- .../libbravo => libndkqsp/jni}/NDKLib.java | 26 +- .../src/bindings/android/android_callbacks.c | 32 +- .../jni/src/bindings/android/android_coding.c | 48 ++- .../src/bindings/android/android_control.c | 339 +++++----------- .../android/org_libndkqsp_jni_NDKLib.h | 373 ++++++++++++++++++ ..._android_questopiabundle_libbravo_NDKLib.h | 373 ------------------ .../jni/src/bindings/android/qsp_android.h | 16 +- 7 files changed, 550 insertions(+), 657 deletions(-) rename libbravo/src/main/java/org/{qp/android/questopiabundle/libbravo => libndkqsp/jni}/NDKLib.java (82%) create mode 100644 libbravo/src/main/jni/src/bindings/android/org_libndkqsp_jni_NDKLib.h delete mode 100644 libbravo/src/main/jni/src/bindings/android/org_qp_android_questopiabundle_libbravo_NDKLib.h diff --git a/libbravo/src/main/java/org/qp/android/questopiabundle/libbravo/NDKLib.java b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java similarity index 82% rename from libbravo/src/main/java/org/qp/android/questopiabundle/libbravo/NDKLib.java rename to libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java index 34decdd..4e6560b 100644 --- a/libbravo/src/main/java/org/qp/android/questopiabundle/libbravo/NDKLib.java +++ b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java @@ -1,22 +1,10 @@ -package org.qp.android.questopiabundle.libbravo; - -import java.util.Objects; +package org.libndkqsp.jni; public abstract class NDKLib { - public class ListItem { - public String pathToImage; - public String text; + public record ListItem(String image, String text) { } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - var that = (ListItem) o; - return Objects.equals(text, that.text) - && Objects.equals(pathToImage, that.pathToImage); - } - } + public record ExecutionState(String loc, int actIndex, int lineNum) { } public record VarValResp(boolean isSuccess , String stringValue , int intValue) { } @@ -51,13 +39,13 @@ public record ErrorData(String locName , int errorNum , int index , int line) { public native void QSPSetInputStrText(String val); /* Actions */ public native int QSPGetActionsCount(); - public native Object QSPGetActionData(int ind);//!!!STUB + public native ListItem[] QSPGetActionData(); public native boolean QSPExecuteSelActionCode(boolean isRefresh); public native boolean QSPSetSelActionIndex(int ind, boolean isRefresh); public native int QSPGetSelActionIndex(); public native boolean QSPIsActionsChanged(); /* Objects */ - public native Object QSPGetObjectData(int ind);//!!!STUB + public native ListItem[] QSPGetObjectData(); public native int QSPGetObjectsCount(); public native boolean QSPSetSelObjectIndex(int ind, boolean isRefresh); public native int QSPGetSelObjectIndex(); @@ -68,11 +56,11 @@ public record ErrorData(String locName , int errorNum , int index , int line) { public native boolean QSPExecCounter(boolean isRefresh); public native boolean QSPExecUserInput(boolean isRefresh); /* Errors */ - public native Object QSPGetLastErrorData(); + public native ErrorData QSPGetLastErrorData(); public native String QSPGetErrorDesc(int errorNum); /* Game */ public native boolean QSPLoadGameWorld(String fileName); - public native boolean QSPLoadGameWorldFromData(byte[] data , int dataSize, String fileName); + public native boolean QSPLoadGameWorldFromData(byte[] data, String fileName); public native boolean QSPSaveGame(String fileName, boolean isRefresh); public native byte[] QSPSaveGameAsData(boolean isRefresh); public native boolean QSPOpenSavedGame(String fileName, boolean isRefresh); diff --git a/libbravo/src/main/jni/src/bindings/android/android_callbacks.c b/libbravo/src/main/jni/src/bindings/android/android_callbacks.c index 563a2a5..da1a336 100644 --- a/libbravo/src/main/jni/src/bindings/android/android_callbacks.c +++ b/libbravo/src/main/jni/src/bindings/android/android_callbacks.c @@ -57,7 +57,7 @@ void qspCallDebug(QSP_CHAR* str) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, str); + jstring qspText = ndkToJavaString(javaEnv, str); qspSaveCallState(&state, QSP_TRUE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_DEBUG], qspText); @@ -101,7 +101,7 @@ void qspCallSetInputStrText(QSP_CHAR* text) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, text); + jstring qspText = ndkToJavaString(javaEnv, text); qspSaveCallState(&state, QSP_TRUE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_SETINPUTSTRTEXT], qspText); @@ -116,8 +116,8 @@ void qspCallAddMenuItem(QSP_CHAR* name, QSP_CHAR* imgPath) if (qspCallBacks[QSP_CALL_ADDMENUITEM]) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring menuItemName = qspToJVMString(javaEnv, name); - jstring menuItemImg = qspToJVMString(javaEnv, imgPath); + jstring menuItemName = ndkToJavaString(javaEnv, name); + jstring menuItemImg = ndkToJavaString(javaEnv, imgPath); qspSaveCallState(&state, QSP_TRUE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_ADDMENUITEM], menuItemName, menuItemImg); @@ -134,7 +134,7 @@ void qspCallSystem(QSP_CHAR* cmd) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, cmd); + jstring qspText = ndkToJavaString(javaEnv, cmd); qspSaveCallState(&state, QSP_TRUE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_SYSTEM], qspText); @@ -149,7 +149,7 @@ void qspCallOpenGame(QSP_CHAR* file) if (qspCallBacks[QSP_CALL_OPENGAMESTATUS]) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, file); + jstring qspText = ndkToJavaString(javaEnv, file); qspSaveCallState(&state, QSP_FALSE, QSP_TRUE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_OPENGAMESTATUS], qspText); @@ -164,7 +164,7 @@ void qspCallSaveGame(QSP_CHAR* file) if (qspCallBacks[QSP_CALL_SAVEGAMESTATUS]) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, file); + jstring qspText = ndkToJavaString(javaEnv, file); qspSaveCallState(&state, QSP_FALSE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_SAVEGAMESTATUS], qspText); @@ -179,7 +179,7 @@ void qspCallShowMessage(QSP_CHAR* text) if (qspCallBacks[QSP_CALL_SHOWMSGSTR]) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, text); + jstring qspText = ndkToJavaString(javaEnv, text); qspSaveCallState(&state, QSP_FALSE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_SHOWMSGSTR], qspText); @@ -206,7 +206,7 @@ void qspCallShowPicture(QSP_CHAR* file) if (qspCallBacks[QSP_CALL_SHOWIMAGE]) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, file); + jstring qspText = ndkToJavaString(javaEnv, file); qspSaveCallState(&state, QSP_FALSE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_SHOWIMAGE], qspText); @@ -233,7 +233,7 @@ void qspCallPlayFile(QSP_CHAR* file, int volume) if (qspCallBacks[QSP_CALL_PLAYFILE]) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, file); + jstring qspText = ndkToJavaString(javaEnv, file); qspSaveCallState(&state, QSP_FALSE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_PLAYFILE], qspText, volume); @@ -249,7 +249,7 @@ QSP_BOOL qspCallIsPlayingFile(QSP_CHAR* file) QSPCallState state; QSP_BOOL isPlaying; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, file); + jstring qspText = ndkToJavaString(javaEnv, file); qspSaveCallState(&state, QSP_FALSE, QSP_FALSE); isPlaying = (QSP_BOOL)(*javaEnv)->CallBooleanMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_ISPLAYINGFILE], qspText); @@ -294,7 +294,7 @@ void qspCallCloseFile(QSP_CHAR* file) if (qspCallBacks[QSP_CALL_CLOSEFILE]) { QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, file); + jstring qspText = ndkToJavaString(javaEnv, file); qspSaveCallState(&state, QSP_FALSE, QSP_FALSE); (*javaEnv)->CallVoidMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_CLOSEFILE], qspText); @@ -322,13 +322,13 @@ QSP_CHAR* qspCallInputBox(QSP_CHAR* text) QSPCallState state; QSP_CHAR* buffer; JNIEnv *javaEnv = ndkGetJniEnv(); - jstring qspText = qspToJVMString(javaEnv, text); + jstring qspText = ndkToJavaString(javaEnv, text); qspSaveCallState(&state, QSP_TRUE, QSP_FALSE); jstring jResult = (jstring)((*javaEnv)->CallObjectMethod(javaEnv, ndkApiObject, qspCallBacks[QSP_CALL_INPUTBOX], qspText)); const char* str = (*javaEnv)->GetStringUTFChars(javaEnv, jResult, NULL); if (str != NULL) - buffer = qspC2W(str); + buffer = ndkFromJavaString(javaEnv, jResult); else buffer = qspGetNewText(QSP_FMT(""), 0); (*javaEnv)->ReleaseStringUTFChars(javaEnv, jResult, str); @@ -346,7 +346,7 @@ char* qspCallGetFileContents(QSP_CHAR* fileName, int* filesize) QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); // Convert QSP file name to Java - jstring javaFileName = qspToJVMString(javaEnv, fileName); + jstring javaFileName = ndkToJavaString(javaEnv, fileName); qspSaveCallState(&state, QSP_TRUE, QSP_FALSE); // Call GetFileContents @@ -380,7 +380,7 @@ void qspCallChangeQuestPath(QSP_CHAR* path) QSPCallState state; JNIEnv *javaEnv = ndkGetJniEnv(); // Convert QSP path to Java - jstring qspText = qspToJVMString(javaEnv, path); + jstring qspText = ndkToJavaString(javaEnv, path); qspSaveCallState(&state, QSP_FALSE, QSP_FALSE); // Call ChangeQuestPath diff --git a/libbravo/src/main/jni/src/bindings/android/android_coding.c b/libbravo/src/main/jni/src/bindings/android/android_coding.c index 0040e86..cf4ef8f 100644 --- a/libbravo/src/main/jni/src/bindings/android/android_coding.c +++ b/libbravo/src/main/jni/src/bindings/android/android_coding.c @@ -162,27 +162,49 @@ QSP_CHAR *qspC2W(char *src) return dst; } -jstring qspToJVMString(JNIEnv *env, QSP_CHAR *str) +jstring ndkToJavaString(JNIEnv *env, QSP_CHAR *str) { + if (!str) return 0; return (*env)->NewString(env, (jchar *)str, qspStrLen(str)); } -QSP_CHAR qspFromJavaString(JNIEnv *env, jstring str) +QSP_CHAR *ndkFromJavaString(JNIEnv *env, jstring str) { -// jsize length; -// jchar *chars; -// QSP_CHAR res; -// length = (*env)->GetStringLength(env, str); -// chars = (*env)->GetStringChars(env, str, 0); -// res = qspGetNewText(qspC2W(chars), length); -// (*env)->ReleaseStringChars(env, str, chars); -// return res; - return 0; + if (!str) return 0; + jsize length; + jchar *chars; + QSP_CHAR *res; + length = (*env)->GetStringLength(env, str); + chars = (*env)->GetStringChars(env, str, 0); + res = qspGetNewText(chars, length); + (*env)->ReleaseStringChars(env, str, chars); + return res; } -char *qspToSysString(QSP_CHAR *s) +JNIListItem ndkToJavaListItem(JNIEnv *env, QSP_CHAR *image, QSP_CHAR *text) { - return qspW2C(s); + JNIListItem res; + jfieldID fieldId; + jobject jniListItem = (*env)->AllocObject(env, ndkListItemClass); + + res.ListItem = jniListItem; + res.Image = ndkToJavaString(env, image); + res.Name = ndkToJavaString(env, text); + + fieldId = (*env)->GetFieldID(env, ndkListItemClass, "image", "Ljava/lang/String;"); + (*env)->SetObjectField(env, jniListItem, fieldId, res.Image); + + fieldId = (*env)->GetFieldID(env, ndkListItemClass, "text", "Ljava/lang/String;"); + (*env)->SetObjectField(env, jniListItem, fieldId, res.Name); + + return res; +} + +void ndkReleaseJavaListItem(JNIEnv *env, JNIListItem *listItem) +{ + (*env)->DeleteLocalRef(env, listItem->ListItem); + (*env)->DeleteLocalRef(env, listItem->Image); + (*env)->DeleteLocalRef(env, listItem->Name); } #endif diff --git a/libbravo/src/main/jni/src/bindings/android/android_control.c b/libbravo/src/main/jni/src/bindings/android/android_control.c index 0f93f7e..b7670e9 100644 --- a/libbravo/src/main/jni/src/bindings/android/android_control.c +++ b/libbravo/src/main/jni/src/bindings/android/android_control.c @@ -45,7 +45,7 @@ jclass ndkErrorInfoClass; jclass ndkVarValResp; /* ------------------------------------------------------------ */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsInCallBack(JNIEnv *env, jobject this) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsInCallBack(JNIEnv *env, jobject this) { return qspIsInCallBack; } @@ -53,83 +53,74 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q /* Debugging */ /* Managing the debugging mode */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPEnableDebugMode(JNIEnv *env, jobject this, jboolean isDebug) +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPEnableDebugMode(JNIEnv *env, jobject this, jboolean isDebug) { qspIsDebug = isDebug; } /* Getting current state data */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetCurStateData(JNIEnv *env, jobject this) +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetCurStateData(JNIEnv *env, jobject this) { - //!!!STUB -// *loc = (qspRealCurLoc >= 0 && qspRealCurLoc < qspLocsCount ? qspLocs[qspRealCurLoc].Name : 0); -// *actIndex = qspRealActIndex; -// *line = qspRealLine; - return NULL; + jfieldID fieldId; + QSP_CHAR *locName; + jobject jniExecutionState = (*env)->AllocObject(env, ndkExecutionStateClass); + + locName = ((qspRealCurLoc >= 0 && qspRealCurLoc < qspLocsCount) ? qspLocs[qspRealCurLoc].Name : 0); + + fieldId = (*env)->GetFieldID(env, ndkExecutionStateClass , "loc", "Ljava/lang/String;"); + (*env)->SetObjectField(env, jniExecutionState, fieldId, ndkToJavaString(env, locName)); + + fieldId = (*env)->GetFieldID(env, ndkExecutionStateClass , "actIndex", "I"); + (*env)->SetIntField(env, jniExecutionState, fieldId, qspRealActIndex); + + fieldId = (*env)->GetFieldID(env, ndkExecutionStateClass , "lineNum", "I"); + (*env)->SetIntField(env, jniExecutionState, fieldId, qspRealLine); + + return jniExecutionState; } /* ------------------------------------------------------------ */ /* Version Information */ /* Version */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVersion(JNIEnv *env, jobject this) +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVersion(JNIEnv *env, jobject this) { - char *sz = qspW2C(QSP_VER); - jstring result = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - return result; + return ndkToJavaString(env, QSP_VER); } /* Date and time of compilation */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetCompiledDateTime(JNIEnv *env, jobject this) +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetCompiledDateTime(JNIEnv *env, jobject this) { - char *sz = qspW2C(QSP_FMT(__DATE__) QSP_FMT(", ") QSP_FMT(__TIME__)); - jstring result = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - return result; + return ndkToJavaString(env, QSP_FMT(__DATE__) QSP_FMT(", ") QSP_FMT(__TIME__)); } /* ------------------------------------------------------------ */ /* Number of full location updates */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetFullRefreshCount(JNIEnv *env, jobject this) +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetFullRefreshCount(JNIEnv *env, jobject this) { return qspFullRefreshCount; } /* ------------------------------------------------------------ */ /* Full path to the downloaded game file */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetQstFullPath(JNIEnv *env, jobject this) +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetQstFullPath(JNIEnv *env, jobject this) { - char *sz = qspW2C(qspQstFullPath); - jstring result = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - return result; + return ndkToJavaString(env, qspQstFullPath); } /* ------------------------------------------------------------ */ /* Name of the current location */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetCurLoc(JNIEnv *env, jobject this) +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetCurLoc(JNIEnv *env, jobject this) { - char *sz = qspW2C((qspCurLoc >= 0 ? qspLocs[qspCurLoc].Name : 0)); - jstring result = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - return result; + return ndkToJavaString(env, qspCurLoc >= 0 ? qspLocs[qspCurLoc].Name : 0); } /* ------------------------------------------------------------ */ /* Basic description of the location */ /* Text of the main location description window */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetMainDesc(JNIEnv *env, jobject this) +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetMainDesc(JNIEnv *env, jobject this) { - char *sz = qspW2C(qspCurDesc); - jstring result = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - return result; + return ndkToJavaString(env, qspCurDesc); } /* The ability to change the text of the main description */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsMainDescChanged(JNIEnv *env, jobject this) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsMainDescChanged(JNIEnv *env, jobject this) { return qspIsMainDescChanged; } @@ -137,17 +128,13 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q /* Additional description of the location */ /* Text of the additional location description window */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarsDesc(JNIEnv *env, jobject this) +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarsDesc(JNIEnv *env, jobject this) { - char *sz = qspW2C(qspCurVars); - jstring result = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - return result; + return ndkToJavaString(env, qspCurVars); } /* Possibility to change the text of the additional description */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsVarsDescChanged(JNIEnv *env, jobject this) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsVarsDescChanged(JNIEnv *env, jobject this) { return qspIsVarsDescChanged; } @@ -155,7 +142,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q /* ------------------------------------------------------------ */ /* Get the value of the specified expression */ //(const QSP_CHAR *expr, QSP_BOOL *isString, int *numVal, QSP_CHAR *strVal, int strValBufSize) -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetExprValue(JNIEnv *env, jobject this) +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetExprValue(JNIEnv *env, jobject this) { //!!!STUB //{ @@ -180,70 +167,36 @@ JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QS } /* ------------------------------------------------------------ */ /* Text of the input line */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSetInputStrText(JNIEnv *env, jobject this, jstring val) +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSetInputStrText(JNIEnv *env, jobject this, jstring val) { - const char *str = (*env)->GetStringUTFChars(env, val, NULL); - if (str == NULL) - return; - QSP_CHAR *strConverted = qspC2W(str); - + QSP_CHAR *strConverted = ndkFromJavaString(env, val); qspCurInputLen = qspAddText(&strConverted, (QSP_CHAR *)val, 0, -1, QSP_FALSE); - - (*env)->ReleaseStringUTFChars(env, val, str); } /* ------------------------------------------------------------ */ /* List of actions */ /* Number of actions */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetActionsCount(JNIEnv *env, jobject this) +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetActionsCount(JNIEnv *env, jobject this) { return qspCurActionsCount; } /* Data actions with the specified index */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetActionData(JNIEnv *env, jobject this, jint ind) +JNIEXPORT jobjectArray JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetActionData(JNIEnv *env, jobject this) { - QSP_CHAR *qspImgFileName; - QSP_CHAR *qspActName; - - if (ind >= 0 && ind < qspCurActionsCount) + int i; + JNIListItem item; + jobjectArray res = (*env)->NewObjectArray(env, qspCurActionsCount, ndkListItemClass, 0); + for (i = 0; i < qspCurActionsCount; ++i) { - qspImgFileName = qspCurActions[ind].Image; - qspActName = qspCurActions[ind].Desc; + item = ndkToJavaListItem(env, qspCurActions[i].Image, qspCurActions[i].Desc); + (*env)->SetObjectArrayElement(env, res, i, item.ListItem); } - else - { - qspImgFileName = qspActName = 0; - } - - jstring actName = NULL; - if (qspActName != NULL) { - actName = qspToJVMString(env, qspActName); - } - - jstring actImg = NULL; - if (qspImgFileName != NULL) { - actImg = qspToJVMString(env, qspImgFileName); - } - - if (ndkListItemClass == 0) - return 0; - - jobject obj = (*env)->AllocObject(env, ndkListItemClass); - jfieldID fid = (*env)->GetFieldID(env, ndkListItemClass, "text", "Ljava/lang/String;"); - jfieldID fid2 = (*env)->GetFieldID(env, ndkListItemClass, "pathToImage", "Ljava/lang/String;"); - - if (fid == 0 || fid2 == 0) - return 0; - - (*env)->SetObjectField(env, obj, fid, actName); - (*env)->SetObjectField(env, obj, fid2, actImg); - - return obj; + return res; } /* Executing the code of the selected action */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecuteSelActionCode(JNIEnv *env, jobject this, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecuteSelActionCode(JNIEnv *env, jobject this, jboolean isRefresh) { if (qspCurSelAction >= 0) { @@ -258,7 +211,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q } /* Set the index of the selected action */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSetSelActionIndex(JNIEnv *env, jobject this, jint ind, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSetSelActionIndex(JNIEnv *env, jobject this, jint ind, jboolean isRefresh) { if (ind >= 0 && ind < qspCurActionsCount && ind != qspCurSelAction) { @@ -274,13 +227,13 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q } /* Get the index of the selected action */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetSelActionIndex(JNIEnv *env, jobject this) +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetSelActionIndex(JNIEnv *env, jobject this) { return qspCurSelAction; } /* Ability to change the list of actions */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsActionsChanged(JNIEnv *env, jobject this) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsActionsChanged(JNIEnv *env, jobject this) { return qspIsActionsChanged; } @@ -288,56 +241,27 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q /* List of objects */ /* Number of objects */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetObjectsCount(JNIEnv *env, jobject this) +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetObjectsCount(JNIEnv *env, jobject this) { return qspCurObjectsCount; } /* Object data with the specified index */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetObjectData(JNIEnv *env, jobject this, jint ind) +JNIEXPORT jobjectArray JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetObjectData(JNIEnv *env, jobject this) { - QSP_CHAR *qspImgFileName; - QSP_CHAR *qspObjName; - - if (ind >= 0 && ind < qspCurObjectsCount) - { - qspImgFileName = qspCurObjects[ind].Image; - qspObjName = qspCurObjects[ind].Desc; - } - else + int i; + JNIListItem item; + jobjectArray res = (*env)->NewObjectArray(env, qspCurActionsCount, ndkListItemClass, 0); + for (i = 0; i < qspCurActionsCount; ++i) { - qspImgFileName = qspObjName = 0; + item = ndkToJavaListItem(env, qspCurActions[i].Image, qspCurActions[i].Desc); + (*env)->SetObjectArrayElement(env, res, i, item.ListItem); } - - jstring objName = NULL; - if (qspObjName != NULL) { - objName = qspToJVMString(env, qspObjName); - } - - jstring objImg = NULL; - if (qspImgFileName != NULL) { - objImg = qspToJVMString(env, qspImgFileName); - } - - if (ndkListItemClass == 0) - return 0; - - jobject obj = (*env)->AllocObject(env, ndkListItemClass); - jfieldID fid = (*env)->GetFieldID(env, ndkListItemClass, "text", "Ljava/lang/String;"); - jfieldID fid2 = (*env)->GetFieldID(env, ndkListItemClass, "pathToImage", "Ljava/lang/String;"); - - if (fid == 0 || fid2 == 0) - return 0; - - // Set the major field to the operating system's major version. - (*env)->SetObjectField(env, obj, fid, objName); - (*env)->SetObjectField(env, obj, fid2, objImg); - - return obj; + return res; } /* Set the index of the selected object */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSetSelObjectIndex(JNIEnv *env, jobject this, jint ind, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSetSelObjectIndex(JNIEnv *env, jobject this, jint ind, jboolean isRefresh) { if (ind >= 0 && ind < qspCurObjectsCount && ind != qspCurSelObject) { @@ -353,19 +277,19 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q } /* Get the index of the selected object */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetSelObjectIndex(JNIEnv *env, jobject this) +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetSelObjectIndex(JNIEnv *env, jobject this) { return qspCurSelObject; } /* Ability to change the list of objects */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsObjectsChanged(JNIEnv *env, jobject this) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsObjectsChanged(JNIEnv *env, jobject this) { return qspIsObjectsChanged; } /* ------------------------------------------------------------ */ /* Show/hide windows */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPShowWindow(JNIEnv *env, jobject this, jint type, jboolean isShow) +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPShowWindow(JNIEnv *env, jobject this, jint type, jboolean isShow) { QSP_BOOL mIsShow = (QSP_BOOL)isShow; switch (type) @@ -389,7 +313,7 @@ JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSh /* Get the number of array elements */ //QSP_BOOL QSPGetVarValuesCount(const QSP_CHAR *name, int *count) -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarValuesCount(JNIEnv *env, jobject this, jstring name) +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarValuesCount(JNIEnv *env, jobject this, jstring name) { //!!!STUB //{ @@ -418,18 +342,15 @@ QSP_BOOL QSPGetVarValues(const QSP_CHAR *name, int ind, int *numVal, QSP_CHAR ** return QSP_TRUE; } -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarValues(JNIEnv *env, jobject this, jstring name, jint ind) +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarValues(JNIEnv *env, jobject this, jstring name, jint ind) { //Convert array name to QSP string - const char *str = (*env)->GetStringUTFChars(env, name, NULL); - if (str == NULL) - return NULL; - QSP_CHAR *strConverted = qspC2W(str); + QSP_CHAR *strConverted = ndkFromJavaString(env, name); //Call QSP function int numVal = 0; - char *strVal; - QSP_BOOL result = QSPGetVarValues(strConverted, (int)ind, &numVal, &strVal); + QSP_CHAR *strVal; + QSP_BOOL result = QSPGetVarValues(strConverted, ind, &numVal, &strVal); // If this class does not exist then return null. if (ndkVarValResp == 0) @@ -441,12 +362,7 @@ JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QS return NULL; if (result == QSP_TRUE) { (*env)->SetBooleanField(env, obj, successFid, JNI_TRUE); - - char *sz = qspW2C(strVal); - jstring jstringVal = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - + jstring jstringVal = ndkToJavaString(env, strVal); jfieldID stringValueFid = (*env)->GetFieldID(env, ndkVarValResp, "stringValue", "Ljava/lang/String;"); if (stringValueFid == 0) return NULL; @@ -460,18 +376,17 @@ JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QS (*env)->SetBooleanField(env, obj, successFid, JNI_FALSE); } - (*env)->ReleaseStringUTFChars(env, name, str); return obj; } /* Get the maximum number of variables */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetMaxVarsCount(JNIEnv *env, jobject this) +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetMaxVarsCount(JNIEnv *env, jobject this) { return QSP_VARSCOUNT; } /* Get the variable name with the specified index */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarNameByIndex(JNIEnv *env, jobject this, jint index) +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarNameByIndex(JNIEnv *env, jobject this, jint index) { //!!!STUB //{ @@ -485,12 +400,9 @@ JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QS /* Code Execution */ /* Executing a line of code */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecString(JNIEnv *env, jobject this, jstring s, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecString(JNIEnv *env, jobject this, jstring s, jboolean isRefresh) { - const char *str = (*env)->GetStringUTFChars(env, s, NULL); - if (str == NULL) - return JNI_FALSE; - QSP_CHAR *strConverted = qspC2W(str); + QSP_CHAR *strConverted = ndkFromJavaString(env, s); if (qspIsExitOnError && qspErrorNum) return QSP_FALSE; qspPrepareExecution(); @@ -499,17 +411,13 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q if (qspErrorNum) return QSP_FALSE; if ((QSP_BOOL)isRefresh) qspCallRefreshInt(QSP_FALSE); - (*env)->ReleaseStringUTFChars(env, s, str); return QSP_TRUE; } /* Executing the code of the specified location */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecLocationCode(JNIEnv *env, jobject this, jstring name, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecLocationCode(JNIEnv *env, jobject this, jstring name, jboolean isRefresh) { - const char *str = (*env)->GetStringUTFChars(env, name, NULL); - if (str == NULL) - return JNI_FALSE; - QSP_CHAR *strConverted = qspC2W(str); + QSP_CHAR *strConverted = ndkFromJavaString(env, name); if (qspIsExitOnError && qspErrorNum) return QSP_FALSE; qspPrepareExecution(); @@ -518,12 +426,11 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q if (qspErrorNum) return QSP_FALSE; if ((QSP_BOOL)isRefresh) qspCallRefreshInt(QSP_FALSE); - (*env)->ReleaseStringUTFChars(env, name, str); return JNI_TRUE; } /* Execution of the location-counter code */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecCounter(JNIEnv *env, jobject this, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecCounter(JNIEnv *env, jobject this, jboolean isRefresh) { if (!qspIsInCallBack) { @@ -536,7 +443,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q } /* Execution of the code of the input line handler location */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecUserInput(JNIEnv *env, jobject this, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecUserInput(JNIEnv *env, jobject this, jboolean isRefresh) { if (qspIsExitOnError && qspErrorNum) return JNI_FALSE; qspPrepareExecution(); @@ -550,7 +457,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q /* Errors */ /* Get information about the latest error */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetLastErrorData(JNIEnv *env, jobject this) +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetLastErrorData(JNIEnv *env, jobject this) { if (ndkErrorInfoClass == 0) return NULL; @@ -564,24 +471,17 @@ JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QS jobject obj = (*env)->AllocObject(env, ndkErrorInfoClass); int errorNum = qspErrorNum; - char *errorLoc = (qspErrorLoc >= 0 && qspErrorLoc < qspLocsCount ? qspLocs[qspErrorLoc].Name : 0); - int errorActIndex = qspErrorActIndex; - int errorLine = qspErrorLine; - - char *sz = qspW2C(errorLoc); - jstring jLocName = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); + QSP_CHAR *errorLoc = (qspErrorLoc >= 0 && qspErrorLoc < qspLocsCount ? qspLocs[qspErrorLoc].Name : 0); - (*env)->SetObjectField(env, obj, fid, jLocName); + (*env)->SetObjectField(env, obj, fid, ndkToJavaString(env, errorLoc)); (*env)->SetIntField(env, obj, fid2, errorNum); - (*env)->SetIntField(env, obj, fid3, errorActIndex); - (*env)->SetIntField(env, obj, fid4, errorLine); + (*env)->SetIntField(env, obj, fid3, qspErrorActIndex); + (*env)->SetIntField(env, obj, fid4, qspErrorLine); return obj; } /* Get a description of the error by its number */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetErrorDesc(JNIEnv *env, jobject this, jint errorNum) +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetErrorDesc(JNIEnv *env, jobject this, jint errorNum) { QSP_CHAR *str; switch (errorNum) @@ -615,15 +515,11 @@ JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QS default: str = QSP_FMT("Unknown error!"); break; } - char *sz = qspW2C(str); - jstring result = (*env)->NewStringUTF(env, sz); - if (sz != NULL) - free(sz); - return result; + return ndkToJavaString(env, str); } /* ------------------------------------------------------------ */ /* Menu */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSelectMenuItem(JNIEnv *env, jobject this, jint ind) +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSelectMenuItem(JNIEnv *env, jobject this, jint ind) { QSPVariant arg; if (ind >= 0 && ind < qspCurMenuItems) @@ -640,7 +536,7 @@ JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSe /* Working with files */ /* Downloading a new game from a file */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPLoadGameWorld(JNIEnv *env, jobject this, jstring fileName) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPLoadGameWorld(JNIEnv *env, jobject this, jstring fileName) { const char *str = (*env)->GetStringUTFChars(env, fileName, NULL); if (str == NULL) @@ -657,7 +553,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q } /* Saving the state to a file */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSaveGame(JNIEnv *env, jobject this, jstring fileName, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSaveGame(JNIEnv *env, jobject this, jstring fileName, jboolean isRefresh) { const char *str = (*env)->GetStringUTFChars(env, fileName, NULL); if (str == NULL) @@ -675,7 +571,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q } /* Loading status from a file */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPOpenSavedGame(JNIEnv *env, jobject this, jstring fileName, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPOpenSavedGame(JNIEnv *env, jobject this, jstring fileName, jboolean isRefresh) { const char *str = (*env)->GetStringUTFChars(env, fileName, NULL); if (str == NULL) @@ -695,7 +591,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q /* Working with memory */ // /* Loading a new game from memory */ -QSP_BOOL QSPLoadGameWorldFromData(const char *data, int dataSize, const QSP_CHAR *fileName) +QSP_BOOL QSPLoadGameWorldFromData(void *data, int dataSize, const QSP_CHAR *fileName) { char *ptr; if (qspIsExitOnError && qspErrorNum) return QSP_FALSE; @@ -709,35 +605,14 @@ QSP_BOOL QSPLoadGameWorldFromData(const char *data, int dataSize, const QSP_CHAR if (qspErrorNum) return QSP_FALSE; return QSP_TRUE; } -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPLoadGameWorldFromData(JNIEnv *env, jobject this, jbyteArray data, jint dataSize, jstring fileName) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPLoadGameWorldFromData(JNIEnv *env, jobject this, jbyteArray data, jstring fileName) { - //converting data - jbyte *jbuf = malloc(dataSize * sizeof(jbyte)); - if (jbuf == NULL) - return JNI_FALSE; - - (*env)->GetByteArrayRegion(env, data, 0, dataSize, jbuf); - int size = dataSize; - char *mydata = (char *)jbuf; - - /* assume the prompt string and user input has less than 128 - characters */ - int fileNameLen = (*env)->GetStringLength(env, fileName) + 1; - char buf[fileNameLen]; - const jbyte *str; - str = (*env)->GetStringUTFChars(env, fileName, NULL); - if (str == NULL) { - free(jbuf); - return JNI_FALSE; /* OutOfMemoryError already thrown */ - } - - QSP_CHAR *wcs = qspC2W(str); - jboolean result = QSPLoadGameWorldFromData(mydata, size, wcs); - (*env)->ReleaseStringUTFChars(env, fileName, str); - free(wcs); - - free(jbuf); - return result; + /* We don't execute any game code here */ + jint datSize = (*env)->GetArrayLength(env, data); + jbyte *arrayData = (*env)->GetByteArrayElements(env, data, 0); + QSP_BOOL res = QSPLoadGameWorldFromData(arrayData, datSize, fileName); + (*env)->ReleaseByteArrayElements(env, data, arrayData, JNI_ABORT); + return res; } /* Saving state to memory */ @@ -768,7 +643,7 @@ QSP_BOOL QSPSaveGameAsData(void **buf, int *realSize, QSP_BOOL isRefresh) if (isRefresh) qspCallRefreshInt(QSP_FALSE); return QSP_TRUE; } -JNIEXPORT jbyteArray JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSaveGameAsData(JNIEnv *env, jobject this, jboolean isRefresh) +JNIEXPORT jbyteArray JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSaveGameAsData(JNIEnv *env, jobject this, jboolean isRefresh) { void *buffer = NULL; int bufferSize = 0; @@ -803,7 +678,7 @@ QSP_BOOL QSPOpenSavedGameFromData(const void *data, int dataSize, QSP_BOOL isRef if (isRefresh) qspCallRefreshInt(QSP_FALSE); return QSP_TRUE; } -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPOpenSavedGameFromData(JNIEnv *env, jobject this, jbyteArray data, jint dataSize, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPOpenSavedGameFromData(JNIEnv *env, jobject this, jbyteArray data, jint dataSize, jboolean isRefresh) { //converting data jbyte *jbuf = malloc(dataSize * sizeof(jbyte)); @@ -833,7 +708,7 @@ QSP_BOOL QSPOpenSavedGameFromString(const QSP_CHAR* str, QSP_BOOL isRefresh) } /* Restarting the game */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPRestartGame(JNIEnv *env, jobject this, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPRestartGame(JNIEnv *env, jobject this, jboolean isRefresh) { if (qspIsExitOnError && qspErrorNum) return JNI_FALSE; qspPrepareExecution(); @@ -851,7 +726,7 @@ JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_Q //} /* ------------------------------------------------------------ */ /* Initialization */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPInit(JNIEnv *env, jobject this) +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPInit(JNIEnv *env, jobject this) { qspIsDebug = QSP_FALSE; qspRefreshCount = qspFullRefreshCount = 0; @@ -881,20 +756,20 @@ JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIn /* Get JVM references */ (*env)->GetJavaVM(env, &ndkJvm); - clazz = (*env)->FindClass(env, "org/qp/android/questopiabundle/libbravo/NDKLib"); + clazz = (*env)->FindClass(env, "org/libndkqsp/jni/NDKLib"); ndkApiClass = (jclass)(*env)->NewGlobalRef(env, clazz); ndkApiObject = (jobject)(*env)->NewGlobalRef(env, this); - clazz = (*env)->FindClass(env, "org/qp/android/questopiabundle/libbravo/NDKLib$ListItem"); + clazz = (*env)->FindClass(env, "org/libndkqsp/jni/NDKLib$ListItem"); ndkListItemClass = (jclass)(*env)->NewGlobalRef(env, clazz); -// clazz = (*env)->FindClass(env, "org/qp/android/model/lib/QSPLib$ExecutionState"); -// ndkExecutionStateClass = (jclass)(*env)->NewGlobalRef(env, clazz); + clazz = (*env)->FindClass(env, "org/libndkqsp/jni/NDKLib$ExecutionState"); + ndkExecutionStateClass = (jclass)(*env)->NewGlobalRef(env, clazz); - clazz = (*env)->FindClass(env, "org/qp/android/questopiabundle/libbravo/NDKLib$ErrorData"); + clazz = (*env)->FindClass(env, "org/libndkqsp/jni/NDKLib$ErrorData"); ndkErrorInfoClass = (jclass)(*env)->NewGlobalRef(env, clazz); - clazz = (*env)->FindClass(env, "org/qp/android/questopiabundle/libbravo/NDKLib$VarValResp"); + clazz = (*env)->FindClass(env, "org/libndkqsp/jni/NDKLib$VarValResp"); ndkVarValResp = (jclass)(*env)->NewGlobalRef(env, clazz); /* Get references to callbacks */ @@ -922,7 +797,7 @@ JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIn } /* Deinitialization */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPDeInit(JNIEnv *env, jobject this) +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPDeInit(JNIEnv *env, jobject this) { qspMemClear(QSP_FALSE); qspCreateWorld(0, 0); diff --git a/libbravo/src/main/jni/src/bindings/android/org_libndkqsp_jni_NDKLib.h b/libbravo/src/main/jni/src/bindings/android/org_libndkqsp_jni_NDKLib.h new file mode 100644 index 0000000..691c49d --- /dev/null +++ b/libbravo/src/main/jni/src/bindings/android/org_libndkqsp_jni_NDKLib.h @@ -0,0 +1,373 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_libndkqsp_jni_NDKLib */ + +#ifndef _Included_org_libndkqsp_jni_NDKLib +#define _Included_org_libndkqsp_jni_NDKLib +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPInit + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPInit + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPDeInit + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPDeInit + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPIsInCallBack + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsInCallBack + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPEnableDebugMode + * Signature: (Z)V + */ +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPEnableDebugMode + (JNIEnv *, jobject, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetCurStateData + * Signature: ()Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetCurStateData + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetVersion + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVersion + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetCompiledDateTime + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetCompiledDateTime + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetFullRefreshCount + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetFullRefreshCount + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetQstFullPath + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetQstFullPath + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetCurLoc + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetCurLoc + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetExprValue + * Signature: ()Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetExprValue + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetMainDesc + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetMainDesc + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPIsMainDescChanged + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsMainDescChanged + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetVarsDesc + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarsDesc + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPIsVarsDescChanged + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsVarsDescChanged + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetVarValuesCount + * Signature: (Ljava/lang/String;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarValuesCount + (JNIEnv *, jobject, jstring); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetVarValues + * Signature: (Ljava/lang/String;I)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarValues + (JNIEnv *, jobject, jstring, jint); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetMaxVarsCount + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetMaxVarsCount + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetVarNameByIndex + * Signature: (I)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetVarNameByIndex + (JNIEnv *, jobject, jint); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPSetInputStrText + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSetInputStrText + (JNIEnv *, jobject, jstring); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetActionsCount + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetActionsCount + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetActionData + * Signature: ()Ljava/lang/Object; + */ +JNIEXPORT jobjectArray JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetActionData + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPExecuteSelActionCode + * Signature: (Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecuteSelActionCode + (JNIEnv *, jobject, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPSetSelActionIndex + * Signature: (IZ)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSetSelActionIndex + (JNIEnv *, jobject, jint, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetSelActionIndex + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetSelActionIndex + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPIsActionsChanged + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsActionsChanged + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetObjectData + * Signature: ()Ljava/lang/Object; + */ +JNIEXPORT jobjectArray JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetObjectData + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetObjectsCount + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetObjectsCount + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPSetSelObjectIndex + * Signature: (IZ)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSetSelObjectIndex + (JNIEnv *, jobject, jint, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetSelObjectIndex + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetSelObjectIndex + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPIsObjectsChanged + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPIsObjectsChanged + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPExecString + * Signature: (Ljava/lang/String;Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecString + (JNIEnv *, jobject, jstring, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPExecLocationCode + * Signature: (Ljava/lang/String;Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecLocationCode + (JNIEnv *, jobject, jstring, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPExecCounter + * Signature: (Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecCounter + (JNIEnv *, jobject, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPExecUserInput + * Signature: (Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPExecUserInput + (JNIEnv *, jobject, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetLastErrorData + * Signature: ()Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetLastErrorData + (JNIEnv *, jobject); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPGetErrorDesc + * Signature: (I)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetErrorDesc + (JNIEnv *, jobject, jint); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPLoadGameWorld + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPLoadGameWorld + (JNIEnv *, jobject, jstring); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPLoadGameWorldFromData + * Signature: ([BLjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPLoadGameWorldFromData + (JNIEnv *, jobject, jbyteArray, jstring); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPSaveGame + * Signature: (Ljava/lang/String;Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSaveGame + (JNIEnv *, jobject, jstring, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPSaveGameAsData + * Signature: (Z)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSaveGameAsData + (JNIEnv *, jobject, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPOpenSavedGame + * Signature: (Ljava/lang/String;Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPOpenSavedGame + (JNIEnv *, jobject, jstring, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPOpenSavedGameFromData + * Signature: ([BIZ)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPOpenSavedGameFromData + (JNIEnv *, jobject, jbyteArray, jint, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPRestartGame + * Signature: (Z)Z + */ +JNIEXPORT jboolean JNICALL Java_org_libndkqsp_jni_NDKLib_QSPRestartGame + (JNIEnv *, jobject, jboolean); + +/* + * Class: org_libndkqsp_jni_NDKLib + * Method: QSPSelectMenuItem + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_org_libndkqsp_jni_NDKLib_QSPSelectMenuItem + (JNIEnv *, jobject, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libbravo/src/main/jni/src/bindings/android/org_qp_android_questopiabundle_libbravo_NDKLib.h b/libbravo/src/main/jni/src/bindings/android/org_qp_android_questopiabundle_libbravo_NDKLib.h deleted file mode 100644 index 5eec0f6..0000000 --- a/libbravo/src/main/jni/src/bindings/android/org_qp_android_questopiabundle_libbravo_NDKLib.h +++ /dev/null @@ -1,373 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_qp_android_questopiabundle_libbravo_NDKLib */ - -#ifndef _Included_org_qp_android_questopiabundle_libbravo_NDKLib -#define _Included_org_qp_android_questopiabundle_libbravo_NDKLib -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPInit - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPInit - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPDeInit - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPDeInit - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPIsInCallBack - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsInCallBack - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPEnableDebugMode - * Signature: (Z)V - */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPEnableDebugMode - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetCurStateData - * Signature: ()Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetCurStateData - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetVersion - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVersion - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetCompiledDateTime - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetCompiledDateTime - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetFullRefreshCount - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetFullRefreshCount - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetQstFullPath - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetQstFullPath - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetCurLoc - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetCurLoc - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetExprValue - * Signature: ()Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetExprValue - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetMainDesc - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetMainDesc - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPIsMainDescChanged - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsMainDescChanged - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetVarsDesc - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarsDesc - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPIsVarsDescChanged - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsVarsDescChanged - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetVarValuesCount - * Signature: (Ljava/lang/String;)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarValuesCount - (JNIEnv *, jobject, jstring); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetVarValues - * Signature: (Ljava/lang/String;I)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarValues - (JNIEnv *, jobject, jstring, jint); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetMaxVarsCount - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetMaxVarsCount - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetVarNameByIndex - * Signature: (I)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetVarNameByIndex - (JNIEnv *, jobject, jint); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPSetInputStrText - * Signature: (Ljava/lang/String;)V - */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSetInputStrText - (JNIEnv *, jobject, jstring); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetActionsCount - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetActionsCount - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetActionData - * Signature: (I)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetActionData - (JNIEnv *, jobject, jint); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPExecuteSelActionCode - * Signature: (Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecuteSelActionCode - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPSetSelActionIndex - * Signature: (IZ)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSetSelActionIndex - (JNIEnv *, jobject, jint, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetSelActionIndex - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetSelActionIndex - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPIsActionsChanged - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsActionsChanged - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetObjectData - * Signature: (I)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetObjectData - (JNIEnv *, jobject, jint); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetObjectsCount - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetObjectsCount - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPSetSelObjectIndex - * Signature: (IZ)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSetSelObjectIndex - (JNIEnv *, jobject, jint, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetSelObjectIndex - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetSelObjectIndex - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPIsObjectsChanged - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPIsObjectsChanged - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPExecString - * Signature: (Ljava/lang/String;Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecString - (JNIEnv *, jobject, jstring, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPExecLocationCode - * Signature: (Ljava/lang/String;Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecLocationCode - (JNIEnv *, jobject, jstring, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPExecCounter - * Signature: (Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecCounter - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPExecUserInput - * Signature: (Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPExecUserInput - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetLastErrorData - * Signature: ()Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetLastErrorData - (JNIEnv *, jobject); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPGetErrorDesc - * Signature: (I)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPGetErrorDesc - (JNIEnv *, jobject, jint); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPLoadGameWorld - * Signature: (Ljava/lang/String;)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPLoadGameWorld - (JNIEnv *, jobject, jstring); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPLoadGameWorldFromData - * Signature: ([BILjava/lang/String;)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPLoadGameWorldFromData - (JNIEnv *, jobject, jbyteArray, jint, jstring); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPSaveGame - * Signature: (Ljava/lang/String;Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSaveGame - (JNIEnv *, jobject, jstring, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPSaveGameAsData - * Signature: (Z)[B - */ -JNIEXPORT jbyteArray JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSaveGameAsData - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPOpenSavedGame - * Signature: (Ljava/lang/String;Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPOpenSavedGame - (JNIEnv *, jobject, jstring, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPOpenSavedGameFromData - * Signature: ([BIZ)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPOpenSavedGameFromData - (JNIEnv *, jobject, jbyteArray, jint, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPRestartGame - * Signature: (Z)Z - */ -JNIEXPORT jboolean JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPRestartGame - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_qp_android_questopiabundle_libbravo_NDKLib - * Method: QSPSelectMenuItem - * Signature: (I)V - */ -JNIEXPORT void JNICALL Java_org_qp_android_questopiabundle_libbravo_NDKLib_QSPSelectMenuItem - (JNIEnv *, jobject, jint); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/libbravo/src/main/jni/src/bindings/android/qsp_android.h b/libbravo/src/main/jni/src/bindings/android/qsp_android.h index 316a37c..552cbef 100644 --- a/libbravo/src/main/jni/src/bindings/android/qsp_android.h +++ b/libbravo/src/main/jni/src/bindings/android/qsp_android.h @@ -61,7 +61,7 @@ typedef jmethodID QSP_CALLBACK; #include "../qsp.h" - #include "org_qp_android_questopiabundle_libbravo_NDKLib.h" + #include "org_libndkqsp_jni_NDKLib.h" extern JavaVM *ndkJvm; extern jclass ndkApiClass; @@ -72,8 +72,16 @@ extern jclass ndkErrorInfoClass; extern jclass ndkVarValResp; - char *qspW2C(QSP_CHAR *); - QSP_CHAR *qspC2W(char *); - jstring qspToJVMString(JNIEnv *, QSP_CHAR *); + typedef struct + { + jstring Image; + jstring Name; + jobject ListItem; + } JNIListItem; + + jstring ndkToJavaString(JNIEnv *env, QSP_CHAR *str); + QSP_CHAR *ndkFromJavaString(JNIEnv *env, jstring str); + JNIListItem ndkToJavaListItem(JNIEnv *env, QSP_CHAR *image, QSP_CHAR *name); + void ndkReleaseJavaListItem(JNIEnv *env, JNIListItem *listItem); #endif From 15f59f5140f33416799f4de691b52603f8d98f99 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Mon, 10 Feb 2025 05:16:00 +0300 Subject: [PATCH 14/57] Added new methods for working with saves --- .../aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl b/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl index 24a9353..0af82eb 100644 --- a/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl +++ b/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl @@ -1,5 +1,6 @@ package org.qp.android.questopiabundle; +import android.net.Uri; import org.qp.android.questopiabundle.LibResult; import org.qp.android.questopiabundle.LibException; import org.qp.android.questopiabundle.LibDialogRetValue; @@ -17,5 +18,8 @@ interface AsyncCallbacks { void closeFile(String filePath); void playFile(String path, int volume); + void requestPermOnFile(in Uri fileUri); + Uri requestCreateFile(in Uri fileUri, String path); + void onError(in LibException libException); } \ No newline at end of file From d7aabb19c479b590d4bc75637250436aeec7f50c Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Mon, 10 Feb 2025 05:29:05 +0300 Subject: [PATCH 15/57] Rename .java to .kt --- .../questopiabundle/{GameInterface.java => GameInterface.kt} | 0 .../{LibDialogRetValue.java => LibDialogRetValue.kt} | 0 .../questopiabundle/{LibException.java => LibException.kt} | 0 .../qp/android/questopiabundle/{LibResult.java => LibResult.kt} | 0 .../questopiabundle/{QuestopiaBundle.java => QuestopiaBundle.kt} | 0 .../questopiabundle/dto/{LibListItem.java => LibListItem.kt} | 0 .../questopiabundle/dto/{LibMenuItem.java => LibMenuItem.kt} | 0 .../lib/{LibGameRequest.java => LibGameRequest.kt} | 0 .../questopiabundle/lib/{LibGameState.java => LibGameState.kt} | 0 .../questopiabundle/lib/{LibIConfig.java => LibIConfig.kt} | 0 .../android/questopiabundle/lib/{LibIProxy.java => LibIProxy.kt} | 0 .../lib/{LibRefIRequest.java => LibRefIRequest.kt} | 0 .../questopiabundle/lib/{LibTypeDialog.java => LibTypeDialog.kt} | 0 .../questopiabundle/lib/{LibTypeWindow.java => LibTypeWindow.kt} | 0 .../lib/impl/{LibAlphaProxyImpl.java => LibAlphaProxyImpl.kt} | 0 .../lib/impl/{LibBravoProxyImpl.java => LibBravoProxyImpl.kt} | 0 .../lib/impl/{LibCharlieProxyImpl.java => LibCharlieProxyImpl.kt} | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/org/qp/android/questopiabundle/{GameInterface.java => GameInterface.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/{LibDialogRetValue.java => LibDialogRetValue.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/{LibException.java => LibException.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/{LibResult.java => LibResult.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/{QuestopiaBundle.java => QuestopiaBundle.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/dto/{LibListItem.java => LibListItem.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/dto/{LibMenuItem.java => LibMenuItem.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/{LibGameRequest.java => LibGameRequest.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/{LibGameState.java => LibGameState.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/{LibIConfig.java => LibIConfig.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/{LibIProxy.java => LibIProxy.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/{LibRefIRequest.java => LibRefIRequest.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/{LibTypeDialog.java => LibTypeDialog.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/{LibTypeWindow.java => LibTypeWindow.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/impl/{LibAlphaProxyImpl.java => LibAlphaProxyImpl.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/impl/{LibBravoProxyImpl.java => LibBravoProxyImpl.kt} (100%) rename app/src/main/java/org/qp/android/questopiabundle/lib/impl/{LibCharlieProxyImpl.java => LibCharlieProxyImpl.kt} (100%) diff --git a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.java b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/GameInterface.java rename to app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.java b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.java rename to app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibException.java b/app/src/main/java/org/qp/android/questopiabundle/LibException.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/LibException.java rename to app/src/main/java/org/qp/android/questopiabundle/LibException.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibResult.java b/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/LibResult.java rename to app/src/main/java/org/qp/android/questopiabundle/LibResult.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.java b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.java rename to app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.java b/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.java rename to app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.java b/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.java rename to app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt similarity index 100% rename from app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.java rename to app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt From ea11f3f9d6632bc82527daf06ec156c59151d816 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Mon, 10 Feb 2025 05:29:19 +0300 Subject: [PATCH 16/57] Full kotlinization! * Added a new save system --- .../android/questopiabundle/GameInterface.kt | 54 +- .../questopiabundle/LibDialogRetValue.kt | 54 +- .../android/questopiabundle/LibException.kt | 69 +- .../qp/android/questopiabundle/LibResult.kt | 48 +- .../questopiabundle/QuestopiaBundle.kt | 563 ++++++------ .../questopiabundle/dto/LibListItem.kt | 85 +- .../questopiabundle/dto/LibMenuItem.kt | 57 +- .../questopiabundle/lib/LibGameRequest.kt | 40 +- .../questopiabundle/lib/LibGameState.kt | 147 ++- .../android/questopiabundle/lib/LibIConfig.kt | 93 +- .../android/questopiabundle/lib/LibIProxy.kt | 54 +- .../questopiabundle/lib/LibRefIRequest.kt | 86 +- .../questopiabundle/lib/LibTypeDialog.kt | 37 +- .../questopiabundle/lib/LibTypeWindow.kt | 38 +- .../lib/impl/LibAlphaProxyImpl.kt | 786 ++++++++-------- .../lib/impl/LibBravoProxyImpl.kt | 841 ++++++++---------- .../lib/impl/LibCharlieProxyImpl.kt | 808 ++++++++--------- .../android/questopiabundle/utils/FileUtil.kt | 20 - .../android/questopiabundle/utils/HtmlUtil.kt | 1 - .../android/questopiabundle/utils/PathUtil.kt | 3 - .../questopiabundle/utils/StreamUtil.kt | 2 - .../questopiabundle/utils/StringUtil.kt | 4 - .../questopiabundle/utils/ThreadUtil.kt | 2 - 23 files changed, 1716 insertions(+), 2176 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt index ad7af35..81e5ca3 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt @@ -1,37 +1,27 @@ -package org.qp.android.questopiabundle; - -import android.net.Uri; - -import org.qp.android.questopiabundle.lib.LibRefIRequest; -import org.qp.android.questopiabundle.lib.LibTypeDialog; -import org.qp.android.questopiabundle.lib.LibTypeWindow; - -public interface GameInterface { - - boolean isPlayingFile(String filePath); - - void closeAllFiles(); - - void closeFile(String filePath); - - void playFile(String path, int volume); - - void doChangeCurrGameDir(Uri newGameDirUri); - - void doRefresh(LibRefIRequest request); - - LibDialogRetValue showLibDialog(LibTypeDialog dialog, String inputString); - - void changeVisWindow(LibTypeWindow type, boolean show); - +package org.qp.android.questopiabundle + +import android.net.Uri +import org.qp.android.questopiabundle.lib.LibRefIRequest +import org.qp.android.questopiabundle.lib.LibTypeDialog +import org.qp.android.questopiabundle.lib.LibTypeWindow + +interface GameInterface { + fun requestPermFile(pathUri: Uri) + fun requestCreateFile(dirUri: Uri, path: String): Uri + fun isPlayingFile(filePath: String): Boolean + fun closeAllFiles() + fun closeFile(filePath: String?) + fun playFile(path: String?, volume: Int) + fun doChangeCurrGameDir(newGameDirUri: Uri?) + fun doRefresh(request: LibRefIRequest?) + fun showLibDialog(dialog: LibTypeDialog?, inputString: String?): LibDialogRetValue? + fun changeVisWindow(type: LibTypeWindow?, show: Boolean) /** - * Set the counter location processing interval to delayMillis milliseconds. + * Set the counter location processing interval to `delayMillis` milliseconds. */ - void setCountInter(int delayMillis); - + fun setCountInter(delayMillis: Int) /** - * Execute runnable without processing the location counter. + * Execute `runnable` without processing the location counter. */ - void doWithCounterDisabled(Runnable runnable); - + fun doWithCounterDisabled(runnable: Runnable?) } diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt index 54a0838..686cd7b 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt @@ -1,44 +1,32 @@ -package org.qp.android.questopiabundle; +package org.qp.android.questopiabundle -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -import androidx.annotation.NonNull; +data class LibDialogRetValue( + var outTextValue: String = "", + var outNumValue: Int = 0 +) : Parcelable { -public class LibDialogRetValue implements Parcelable { - - public static final Creator CREATOR = new Creator<>() { - @Override - public LibDialogRetValue createFromParcel(Parcel in) { - return new LibDialogRetValue(in); - } - - @Override - public LibDialogRetValue[] newArray(int size) { - return new LibDialogRetValue[size]; - } - }; - - public String outTextValue; - public int outNumValue; - - protected LibDialogRetValue(Parcel in) { - outTextValue = in.readString(); - outNumValue = in.readInt(); + constructor(source: Parcel) : this() { + outTextValue = source.readString() ?: "" + outNumValue = source.readInt() } - public LibDialogRetValue() { + override fun describeContents(): Int = 0 + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(outTextValue) + dest.writeInt(outNumValue) } - @Override - public int describeContents() { - return 0; - } + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibDialogRetValue { + return LibDialogRetValue(source) + } - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(outTextValue); - dest.writeInt(outNumValue); + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibException.kt b/app/src/main/java/org/qp/android/questopiabundle/LibException.kt index dbc3a18..f25f2f3 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/LibException.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/LibException.kt @@ -1,60 +1,39 @@ -package org.qp.android.questopiabundle; +package org.qp.android.questopiabundle -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -public class LibException implements Parcelable { +class LibException( + private val errorMessage: String?, + private val errorCode: Int = RUNTIME_EXCEPTION +) : Parcelable { - public static final int RUNTIME_EXCEPTION = 1000; - public static final int ARITHMETIC_EXCEPTION = 1001; - public static final Creator CREATOR = new Creator<>() { - @Override - public LibException createFromParcel(Parcel in) { - return new LibException(in); - } + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) - @Override - public LibException[] newArray(int size) { - return new LibException[size]; + fun toException(): Exception { + return when (errorCode) { + RUNTIME_EXCEPTION -> RuntimeException(errorMessage) + else -> RuntimeException(errorMessage) } - }; - - private final String errorMessage; - private final int errorCode; - - public LibException(String errorMessage) { - this(errorMessage, RUNTIME_EXCEPTION); } - public LibException(String errorMessage, int errorCode) { - this.errorMessage = errorMessage; - this.errorCode = errorCode; + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(errorMessage) + parcel.writeInt(errorCode) } - protected LibException(Parcel in) { - errorMessage = in.readString(); - errorCode = in.readInt(); + override fun describeContents(): Int { + return 0 } - public Exception toException() { - switch (errorCode) { - case RUNTIME_EXCEPTION: - return new RuntimeException(errorMessage); - case ARITHMETIC_EXCEPTION: - return new ArithmeticException(errorMessage); - default: - return new RuntimeException(errorMessage); + companion object CREATOR : Parcelable.Creator { + const val RUNTIME_EXCEPTION: Int = 1000 + override fun createFromParcel(parcel: Parcel): LibException { + return LibException(parcel) } - } - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(errorMessage); - dest.writeInt(errorCode); + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt b/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt index 349c006..f9583b6 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt @@ -1,39 +1,29 @@ -package org.qp.android.questopiabundle; +package org.qp.android.questopiabundle -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -public class LibResult implements Parcelable { +data class LibResult( + var value: T? +) : Parcelable { - public static final Creator> CREATOR = new Creator<>() { - @Override - public LibResult createFromParcel(Parcel in) { - return new LibResult<>(in); - } - - @Override - public LibResult[] newArray(int size) { - return new LibResult[size]; - } - }; - - public T value; - - public LibResult(T value) { - this.value = value; + constructor(source: Parcel) : this(null) { + this.value = source.readParcelable(javaClass.classLoader) } - protected LibResult(Parcel in) { - this.value = in.readParcelable(getClass().getClassLoader()); + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeParcelable(value, flags) } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(value, flags); - } + override fun describeContents(): Int = 0 + + companion object CREATOR : Parcelable.Creator> { + override fun createFromParcel(source: Parcel): LibResult { + return LibResult(source) + } - @Override - public int describeContents() { - return 0; + override fun newArray(size: Int): Array?> { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index a495682..d04a374 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -1,365 +1,376 @@ -package org.qp.android.questopiabundle; - -import android.app.Service; -import android.content.Intent; -import android.net.Uri; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import androidx.annotation.Nullable; - -import com.anggrayudi.storage.file.DocumentFileCompat; - -import org.qp.android.questopiabundle.lib.LibGameRequest; -import org.qp.android.questopiabundle.lib.impl.LibBravoProxyImpl; -import org.qp.android.questopiabundle.lib.impl.LibAlphaProxyImpl; -import org.qp.android.questopiabundle.lib.LibRefIRequest; -import org.qp.android.questopiabundle.lib.LibTypeDialog; -import org.qp.android.questopiabundle.lib.LibTypeWindow; -import org.qp.android.questopiabundle.lib.impl.LibCharlieProxyImpl; - -public class QuestopiaBundle extends Service implements GameInterface { - - private final Handler counterHandler = new Handler(); - private final Handler counterNDKHandler = new Handler(); - private final Handler counterSNXHandler = new Handler(); - private LibAlphaProxyImpl libAlphaProxy; - private LibBravoProxyImpl libBravoProxy; - private LibCharlieProxyImpl libCharlieProxy; - private AsyncCallbacks callbacks; - private volatile int counterInterval = 500; - private final Runnable counterTask = new Runnable() { - @Override - public void run() { - libAlphaProxy.executeCounter(); - counterHandler.postDelayed(this, counterInterval); +package org.qp.android.questopiabundle + +import android.app.Service +import android.content.Intent +import android.net.Uri +import android.os.Handler +import android.os.IBinder +import android.os.Looper +import android.os.RemoteException +import android.util.Log +import com.anggrayudi.storage.file.DocumentFileCompat.getAccessibleAbsolutePaths +import org.qp.android.questopiabundle.lib.LibGameRequest +import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.lib.LibRefIRequest +import org.qp.android.questopiabundle.lib.LibTypeDialog +import org.qp.android.questopiabundle.lib.LibTypeWindow +import org.qp.android.questopiabundle.lib.impl.LibAlphaProxyImpl +import org.qp.android.questopiabundle.lib.impl.LibBravoProxyImpl +import org.qp.android.questopiabundle.lib.impl.LibCharlieProxyImpl +import kotlin.concurrent.Volatile + +class QuestopiaBundle : Service(), GameInterface { + private val counterHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) + private val counterNDKHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) + private val counterSNXHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) + private lateinit var libAlphaProxy: LibAlphaProxyImpl + private lateinit var libBravoProxy: LibBravoProxyImpl + private lateinit var libCharlieProxy: LibCharlieProxyImpl + private var callbacks: AsyncCallbacks? = null + + @Volatile + private var counterInterval = 500 + private val counterTask: Runnable = object : Runnable { + override fun run() { + libAlphaProxy.executeCounter() + counterHandler.postDelayed(this, counterInterval.toLong()) } - }; - private final Runnable counterNDKTask = new Runnable() { - @Override - public void run() { - libBravoProxy.executeCounter(); - counterNDKHandler.postDelayed(this, counterInterval); + } + private val counterNDKTask: Runnable = object : Runnable { + override fun run() { + libBravoProxy.executeCounter() + counterNDKHandler.postDelayed(this, counterInterval.toLong()) + } + } + private val counterSNXTask: Runnable = object : Runnable { + override fun run() { + libCharlieProxy.executeCounter() + counterSNXHandler.postDelayed(this, counterInterval.toLong()) } - }; - private final Runnable counterSNXTask = new Runnable() { - @Override - public void run() { - libCharlieProxy.executeCounter(); - counterSNXHandler.postDelayed(this, counterInterval); + } + + @Volatile + private var mLibVersion = 0 + + override fun requestPermFile(pathUri: Uri) { + try { + callbacks?.requestPermOnFile(pathUri) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) } - }; - private volatile int mLibVersion; + } - @Override - public boolean isPlayingFile(String filePath) { + override fun requestCreateFile(dirUri: Uri, path: String): Uri { try { - if (callbacks == null) return false; - return callbacks.isPlayingFile(filePath); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); - return false; + return callbacks?.requestCreateFile(dirUri, path) ?: Uri.EMPTY + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + return Uri.EMPTY } } - @Override - public void closeAllFiles() { + override fun isPlayingFile(filePath: String): Boolean { try { - if (callbacks == null) return; - callbacks.closeAllFiles(); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); + return callbacks?.isPlayingFile(filePath) ?: false + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + return false } } - @Override - public void closeFile(String filePath) { + override fun closeAllFiles() { try { - if (callbacks == null) return; - callbacks.closeFile(filePath); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); + callbacks?.closeAllFiles() + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) } } - @Override - public void playFile(String path, int volume) { + override fun closeFile(filePath: String?) { try { - if (callbacks == null) return; - callbacks.playFile(path, volume); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); + callbacks?.closeFile(filePath) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) } } - public void setCallback() { - switch (mLibVersion) { - case 570 -> counterNDKHandler.postDelayed(counterNDKTask, counterInterval); - case 575 -> counterSNXHandler.postDelayed(counterSNXTask, counterInterval); - case 592 -> counterHandler.postDelayed(counterTask, counterInterval); + override fun playFile(path: String?, volume: Int) { + try { + callbacks?.playFile(path, volume) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) } } - public void removeCallback() { - counterHandler.removeCallbacks(counterTask); - counterNDKHandler.removeCallbacks(counterNDKTask); - counterSNXHandler.removeCallbacks(counterSNXTask); + fun setCallback() { + when (mLibVersion) { + 570 -> counterNDKHandler.postDelayed(counterNDKTask, counterInterval.toLong()) + 575 -> counterSNXHandler.postDelayed(counterSNXTask, counterInterval.toLong()) + 592 -> counterHandler.postDelayed(counterTask, counterInterval.toLong()) + } } - @Override - public void doChangeCurrGameDir(Uri newGameDirUri) { - if (callbacks == null) return; + private fun removeCallback() { + counterHandler.removeCallbacks(counterTask) + counterNDKHandler.removeCallbacks(counterNDKTask) + counterSNXHandler.removeCallbacks(counterSNXTask) + } + override fun doChangeCurrGameDir(newGameDirUri: Uri?) { try { - callbacks.sendChangeCurrGameDir(newGameDirUri); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); + callbacks?.sendChangeCurrGameDir(newGameDirUri) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) } } - @Override - public void doRefresh(LibRefIRequest request) { - if (callbacks == null) return; - + override fun doRefresh(request: LibRefIRequest?) { try { - switch (mLibVersion) { - case 570 -> { - callbacks.sendLibRef(new LibResult<>(request)); - callbacks.sendLibGameState(new LibResult<>(libBravoProxy.getGameState())); + when (mLibVersion) { + 570 -> { + callbacks?.sendLibRef(LibResult(request)) + callbacks?.sendLibGameState(LibResult(libBravoProxy.gameState)) } - case 575 -> { - callbacks.sendLibRef(new LibResult<>(request)); - callbacks.sendLibGameState(new LibResult<>(libCharlieProxy.getGameState())); + + 575 -> { + callbacks?.sendLibRef(LibResult(request)) + callbacks?.sendLibGameState(LibResult(libCharlieProxy.gameState)) } - case 592 -> { - callbacks.sendLibRef(new LibResult<>(request)); - callbacks.sendLibGameState(new LibResult<>(libAlphaProxy.getGameState())); + + 592 -> { + callbacks?.sendLibRef(LibResult(request)) + callbacks?.sendLibGameState(LibResult(libAlphaProxy.gameState)) } } - } catch (Exception e) { - Log.e("QuestopiaBundle", "Error", e); + } catch (e: Exception) { + Log.e("QuestopiaBundle", "Error", e) } } - @Override - public LibDialogRetValue showLibDialog(LibTypeDialog dialog, String inputMessage) { - var wrap = new LibDialogRetValue(); - - if (callbacks == null) { - if (dialog == LibTypeDialog.DIALOG_MENU) { - wrap.outNumValue = -1; - } else { - wrap.outTextValue = ""; - } - return wrap; - } + override fun showLibDialog(dialog: LibTypeDialog?, inputString: String?): LibDialogRetValue? { + val wrap = LibDialogRetValue() try { - return callbacks.doOnShowDialog(new LibResult<>(dialog), inputMessage); - } catch (RemoteException e) { - Log.e("QuestopiaBundle", "Error", e); + return callbacks?.doOnShowDialog(LibResult(dialog), inputString) + } catch (e: RemoteException) { + Log.e("QuestopiaBundle", "Error", e) } if (dialog == LibTypeDialog.DIALOG_MENU) { - wrap.outNumValue = -1; + wrap.outNumValue = -1 } else { - wrap.outTextValue = ""; + wrap.outTextValue = "" } - return wrap; + + return wrap } - @Override - public void changeVisWindow(LibTypeWindow type, boolean show) { - if (callbacks == null) return; + override fun changeVisWindow(type: LibTypeWindow?, show: Boolean) { try { - callbacks.doChangeVisWindow(new LibResult<>(type), show); - } catch (RemoteException e) { - Log.e("QuestopiaBundle", "Error", e); + callbacks?.doChangeVisWindow(LibResult(type), show) + } catch (e: RemoteException) { + Log.e("QuestopiaBundle", "Error", e) } } - @Override - public void setCountInter(int delayMillis) { - counterInterval = delayMillis; + override fun setCountInter(delayMillis: Int) { + counterInterval = delayMillis } - @Override - public void doWithCounterDisabled(Runnable runnable) { - switch (mLibVersion) { - case 570 -> { - counterNDKHandler.removeCallbacks(counterNDKTask); - runnable.run(); - counterNDKHandler.postDelayed(counterNDKTask, counterInterval); + override fun doWithCounterDisabled(runnable: Runnable?) { + when (mLibVersion) { + 570 -> { + counterNDKHandler.removeCallbacks(counterNDKTask) + runnable?.run() + counterNDKHandler.postDelayed(counterNDKTask, counterInterval.toLong()) } - case 575 -> { - counterSNXHandler.removeCallbacks(counterSNXTask); - runnable.run(); - counterSNXHandler.postDelayed(counterSNXTask, counterInterval); + + 575 -> { + counterSNXHandler.removeCallbacks(counterSNXTask) + runnable?.run() + counterSNXHandler.postDelayed(counterSNXTask, counterInterval.toLong()) } - case 592 -> { - counterHandler.removeCallbacks(counterTask); - runnable.run(); - counterHandler.postDelayed(counterTask, counterInterval); + + 592 -> { + counterHandler.removeCallbacks(counterTask) + runnable?.run() + counterHandler.postDelayed(counterTask, counterInterval.toLong()) } } } - @Nullable - @Override - public IBinder onBind(Intent intent) { - return new IQuestopiaBundle.Stub() { - @Override - public String versionPlugin() throws RemoteException { - return BuildConfig.VERSION_NAME; - } + override fun onBind(intent: Intent): IBinder = object : IQuestopiaBundle.Stub() { + @Throws(RemoteException::class) + override fun versionPlugin(): String { + return BuildConfig.VERSION_NAME + } - @Override - public String titlePlugin() throws RemoteException { - return "Questopia Bundle"; - } + @Throws(RemoteException::class) + override fun titlePlugin(): String { + return "Questopia Bundle" + } - @Override - public String authorPlugin() throws RemoteException { - return "l3ger0j"; - } + @Throws(RemoteException::class) + override fun authorPlugin(): String { + return "l3ger0j" + } - @Override - public void startNativeLib(int libVer) throws RemoteException { - mLibVersion = libVer; - switch (mLibVersion) { - case 570 -> { - libBravoProxy = new LibBravoProxyImpl(QuestopiaBundle.this); - setCallback(); + @Throws(RemoteException::class) + override fun startNativeLib(libVer: Int) { + mLibVersion = libVer + when (mLibVersion) { + 570 -> { + libBravoProxy = LibBravoProxyImpl(this@QuestopiaBundle) + setCallback() - libBravoProxy.setGameInterface(QuestopiaBundle.this); - libBravoProxy.startLibThread(); - } - case 575 -> { - libCharlieProxy = new LibCharlieProxyImpl(QuestopiaBundle.this); - setCallback(); + libBravoProxy.setGameInterface(this@QuestopiaBundle) + libBravoProxy.startLibThread() + } - libCharlieProxy.setGameInterface(QuestopiaBundle.this); - libCharlieProxy.startLibThread(); - } - case 592 -> { - libAlphaProxy = new LibAlphaProxyImpl(QuestopiaBundle.this); - setCallback(); + 575 -> { + libCharlieProxy = LibCharlieProxyImpl(this@QuestopiaBundle) + setCallback() - libAlphaProxy.setGameInterface(QuestopiaBundle.this); - libAlphaProxy.startLibThread(); - } + libCharlieProxy.setGameInterface(this@QuestopiaBundle) + libCharlieProxy.startLibThread() } - } - @Override - public void stopNativeLib(int libVer) throws RemoteException { - mLibVersion = libVer; - switch (mLibVersion) { - case 570 -> { - libBravoProxy.setGameInterface(null); - libBravoProxy.stopLibThread(); - libBravoProxy = null; - } - case 575 -> { - libCharlieProxy.setGameInterface(null); - libCharlieProxy.stopLibThread(); - libCharlieProxy = null; - } - case 592 -> { - libAlphaProxy.setGameInterface(null); - libAlphaProxy.stopLibThread(); - libAlphaProxy = null; - } + 592 -> { + libAlphaProxy = LibAlphaProxyImpl(this@QuestopiaBundle) + setCallback() + + libAlphaProxy.setGameInterface(this@QuestopiaBundle) + libAlphaProxy.startLibThread() } - stopSelf(); } + } - @Override - public void runGameIntoLib(long gameId, - String gameTitle, - Uri gameDirUri, - Uri gameFileUri) throws RemoteException { - Log.i(this.getClass().getSimpleName(), String.valueOf(DocumentFileCompat.getAccessibleAbsolutePaths(getBaseContext()))); - Log.d(this.getClass().getSimpleName(), "Debug: " + "\nGameID|" + gameId + "\nGameTitle|" + gameTitle + "\nGameDirUri|" + gameDirUri + "\nGameFileUri|" + gameFileUri); - switch (mLibVersion) { - case 570 -> libBravoProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri); - case 575 -> libCharlieProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri); - case 592 -> libAlphaProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri); - } + @Throws(RemoteException::class) + override fun stopNativeLib(libVer: Int) { + mLibVersion = libVer + when (mLibVersion) { + 570 -> libBravoProxy.stopLibThread() + + 575 -> libCharlieProxy.stopLibThread() + + 592 -> libAlphaProxy.stopLibThread() } + stopSelf() + } - @Override - public void onActionClicked(int index) throws RemoteException { - switch (mLibVersion) { - case 570 -> libBravoProxy.onActionClicked(index); - case 575 -> libCharlieProxy.onActionClicked(index); - case 592 -> libAlphaProxy.onActionClicked(index); - } + @Throws(RemoteException::class) + override fun runGameIntoLib( + gameId: Long, + gameTitle: String, + gameDirUri: Uri, + gameFileUri: Uri + ) { + Log.i( + javaClass.simpleName, + checkCallingPermission("android.permission.WRITE_EXTERNAL_STORAGE").toString() + ) + Log.i( + javaClass.simpleName, + checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE").toString() + ) + Log.i( + javaClass.simpleName, getAccessibleAbsolutePaths( + baseContext + ).toString() + ) + Log.d( + javaClass.simpleName, + "Debug: \nGameID|$gameId\nGameTitle|$gameTitle\nGameDirUri|$gameDirUri\nGameFileUri|$gameFileUri" + ) + when (mLibVersion) { + 570 -> libBravoProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri) + + 575 -> libCharlieProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri) + + 592 -> libAlphaProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri) } + } - @Override - public void onObjectClicked(int index) throws RemoteException { - switch (mLibVersion) { - case 570 -> libBravoProxy.onObjectSelected(index); - case 575 -> libCharlieProxy.onObjectSelected(index); - case 592 -> libAlphaProxy.onObjectSelected(index); - } + @Throws(RemoteException::class) + override fun onActionClicked(index: Int) { + when (mLibVersion) { + 570 -> libBravoProxy.onActionClicked(index) + + 575 -> libCharlieProxy.onActionClicked(index) + + 592 -> libAlphaProxy.onActionClicked(index) + } + } + + @Throws(RemoteException::class) + override fun onObjectClicked(index: Int) { + when (mLibVersion) { + 570 -> libBravoProxy.onObjectSelected(index) + + 575 -> libCharlieProxy.onObjectSelected(index) + + 592 -> libAlphaProxy.onObjectSelected(index) } + } - @Override - public void doLibRequest(LibResult gameRequest, String codeToExec, Uri fileUri) throws RemoteException { - switch (mLibVersion) { - case 570 -> { - var libGameReq = (LibGameRequest) gameRequest.value; - switch (libGameReq) { - case LOAD_FILE -> doWithCounterDisabled(() -> libBravoProxy.loadGameState(fileUri)); - case SAVE_FILE -> libBravoProxy.saveGameState(fileUri); - case USE_EXECUTOR -> libBravoProxy.onUseExecutorString(); - case USE_INPUT -> libBravoProxy.onInputAreaClicked(); - case RESTART_GAME -> libBravoProxy.restartGame(); - case EXECUTE_CODE -> libBravoProxy.execute(codeToExec); + @Throws(RemoteException::class) + override fun doLibRequest(gameRequest: LibResult<*>, codeToExec: String, fileUri: Uri) { + when (mLibVersion) { + 570 -> { + val libGameReq = gameRequest.value as LibGameRequest + when (libGameReq) { + LibGameRequest.LOAD_FILE -> doWithCounterDisabled { + libBravoProxy.loadGameState(fileUri) } + + LibGameRequest.SAVE_FILE -> libBravoProxy.saveGameState(fileUri) + LibGameRequest.USE_EXECUTOR -> libBravoProxy.onUseExecutorString() + LibGameRequest.USE_INPUT -> libBravoProxy.onInputAreaClicked() + LibGameRequest.RESTART_GAME -> libBravoProxy.restartGame() + LibGameRequest.EXECUTE_CODE -> libBravoProxy.execute(codeToExec) } - case 575 -> { - var libGameReq = (LibGameRequest) gameRequest.value; - switch (libGameReq) { - case LOAD_FILE -> doWithCounterDisabled(() -> libCharlieProxy.loadGameState(fileUri)); - case SAVE_FILE -> libCharlieProxy.saveGameState(fileUri); - case USE_EXECUTOR -> libCharlieProxy.onUseExecutorString(); - case USE_INPUT -> libCharlieProxy.onInputAreaClicked(); - case RESTART_GAME -> libCharlieProxy.restartGame(); - case EXECUTE_CODE -> libCharlieProxy.execute(codeToExec); + } + + 575 -> { + val libGameReq = gameRequest.value as LibGameRequest + when (libGameReq) { + LibGameRequest.LOAD_FILE -> doWithCounterDisabled { + libCharlieProxy.loadGameState(fileUri) } + + LibGameRequest.SAVE_FILE -> libCharlieProxy.saveGameState(fileUri) + LibGameRequest.USE_EXECUTOR -> libCharlieProxy.onUseExecutorString() + LibGameRequest.USE_INPUT -> libCharlieProxy.onInputAreaClicked() + LibGameRequest.RESTART_GAME -> libCharlieProxy.restartGame() + LibGameRequest.EXECUTE_CODE -> libCharlieProxy.execute(codeToExec) } - case 592 -> { - var libGameReq = (LibGameRequest) gameRequest.value; - switch (libGameReq) { - case LOAD_FILE -> doWithCounterDisabled(() -> libAlphaProxy.loadGameState(fileUri)); - case SAVE_FILE -> libAlphaProxy.saveGameState(fileUri); - case USE_EXECUTOR -> libAlphaProxy.onUseExecutorString(); - case USE_INPUT -> libAlphaProxy.onInputAreaClicked(); - case RESTART_GAME -> libAlphaProxy.restartGame(); - case EXECUTE_CODE -> libAlphaProxy.execute(codeToExec); + } + + 592 -> { + val libGameReq = gameRequest.value as LibGameRequest + when (libGameReq) { + LibGameRequest.LOAD_FILE -> doWithCounterDisabled { + libAlphaProxy.loadGameState(fileUri) } + + LibGameRequest.SAVE_FILE -> libAlphaProxy.saveGameState(fileUri) + LibGameRequest.USE_EXECUTOR -> libAlphaProxy.onUseExecutorString() + LibGameRequest.USE_INPUT -> libAlphaProxy.onInputAreaClicked() + LibGameRequest.RESTART_GAME -> libAlphaProxy.restartGame() + LibGameRequest.EXECUTE_CODE -> libAlphaProxy.execute(codeToExec) } } } + } - @Override - public void sendAsync(AsyncCallbacks callbacks) throws RemoteException { - QuestopiaBundle.this.callbacks = callbacks; - } - }; + @Throws(RemoteException::class) + override fun sendAsync(callbacks: AsyncCallbacks?) { + this@QuestopiaBundle.callbacks = callbacks + } } - @Override - public void onDestroy() { - super.onDestroy(); - libAlphaProxy = null; - libBravoProxy = null; - libCharlieProxy = null; - removeCallback(); + override fun onDestroy() { + super.onDestroy() + removeCallback() } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt index b1119d2..333e7ef 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt @@ -1,70 +1,41 @@ -package org.qp.android.questopiabundle.dto; +package org.qp.android.questopiabundle.dto -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable +import com.libqsp.jni.QSPLib +import org.libndkqsp.jni.NDKLib +import org.libsnxqsp.jni.SNXLib -import androidx.annotation.NonNull; +data class LibListItem( + var text: String = "", + var pathToImage: String = "" +) : Parcelable { -import com.libqsp.jni.QSPLib; + constructor(item: QSPLib.ListItem) : this(item.name, item.image) -import org.libsnxqsp.jni.SNXLib; + constructor(item: NDKLib.ListItem) : this(item.text, item.image) -import java.util.Objects; + constructor(item: SNXLib.ListItem) : this(item.text, item.image) -public class LibListItem implements Parcelable { + constructor(source: Parcel) : this( + source.readString() ?: "", + source.readString() ?: "" + ) - public static final Creator CREATOR = new Creator<>() { - @Override - public LibListItem createFromParcel(Parcel in) { - return new LibListItem(in); - } - - @Override - public LibListItem[] newArray(int size) { - return new LibListItem[size]; - } - }; - - public String text; - public String pathToImage; - - public LibListItem() { - text = ""; - pathToImage = ""; - } - - public LibListItem(QSPLib.ListItem item) { - this.pathToImage = item.image; - this.text = item.name; - } + override fun describeContents(): Int = 0 - public LibListItem(SNXLib.ListItem item) { - this.pathToImage = item.image(); - this.text = item.text(); + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(text) + dest.writeString(pathToImage) } - protected LibListItem(Parcel in) { - text = in.readString(); - pathToImage = in.readString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - var that = (LibListItem) o; - return Objects.equals(text, that.text) - && Objects.equals(pathToImage, that.pathToImage); - } - - @Override - public int describeContents() { - return 0; - } + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibListItem { + return LibListItem(source) + } - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(text); - dest.writeString(pathToImage); + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt index 9da7211..bcd1929 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt @@ -1,45 +1,32 @@ -package org.qp.android.questopiabundle.dto; +package org.qp.android.questopiabundle.dto -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -import androidx.annotation.NonNull; +data class LibMenuItem( + var name: String = "", + var pathToImage: String = "" +) : Parcelable { -public class LibMenuItem implements Parcelable { + constructor(source: Parcel) : this( + source.readString() ?: "", + source.readString() ?: "" + ) - public static final Creator CREATOR = new Creator<>() { - @Override - public LibMenuItem createFromParcel(Parcel in) { - return new LibMenuItem(in); - } - - @Override - public LibMenuItem[] newArray(int size) { - return new LibMenuItem[size]; - } - }; + override fun describeContents(): Int = 0 - public String name; - public String pathToImage; - - public LibMenuItem() { - this.name = ""; - this.pathToImage = ""; + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(name) + dest.writeString(pathToImage) } - protected LibMenuItem(Parcel in) { - this.name = in.readString(); - this.pathToImage = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibMenuItem { + return LibMenuItem(source) + } - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name); - dest.writeString(pathToImage); + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.kt index e1ae6cc..dad489d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.kt @@ -1,11 +1,9 @@ -package org.qp.android.questopiabundle.lib; +package org.qp.android.questopiabundle.lib -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -import androidx.annotation.NonNull; - -public enum LibGameRequest implements Parcelable { +enum class LibGameRequest : Parcelable { LOAD_FILE, SAVE_FILE, USE_EXECUTOR, @@ -14,25 +12,21 @@ public enum LibGameRequest implements Parcelable { EXECUTE_CODE, ; - public static final Creator CREATOR = new Creator<>() { - @Override - public LibGameRequest createFromParcel(Parcel in) { - return LibGameRequest.valueOf(in.readString()); - } - - @Override - public LibGameRequest[] newArray(int size) { - return new LibGameRequest[size]; - } - }; + override fun describeContents(): Int { + return 0 + } - @Override - public int describeContents() { - return 0; + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(name) } - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name()); + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibGameRequest { + return LibGameRequest.valueOf(source.readString()!!) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt index 348d971..91e5f72 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt @@ -1,93 +1,78 @@ -package org.qp.android.questopiabundle.lib; +package org.qp.android.questopiabundle.lib -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable +import org.qp.android.questopiabundle.dto.LibListItem +import org.qp.android.questopiabundle.dto.LibMenuItem +import org.qp.android.questopiabundle.lib.LibIConfig -import org.qp.android.questopiabundle.dto.LibListItem; -import org.qp.android.questopiabundle.dto.LibMenuItem; +data class LibGameState( + var interfaceConfig: LibIConfig = LibIConfig(), + var gameRunning: Boolean = false, + var gameId: Long = 0L, + var gameTitle: String = "", + var gameDirUri: Uri = Uri.EMPTY, + var gameFileUri: Uri = Uri.EMPTY, + var mainDesc: String = "", + var varsDesc: String = "", + var actionsList: MutableList = mutableListOf(), + var objectsList: MutableList = mutableListOf(), + var menuItemsList: MutableList = mutableListOf() +) : Parcelable { -import java.util.ArrayList; -import java.util.List; - -public class LibGameState implements Parcelable { - - public static final Creator CREATOR = new Creator<>() { - @Override - public LibGameState createFromParcel(Parcel in) { - return new LibGameState(in); - } - - @Override - public LibGameState[] newArray(int size) { - return new LibGameState[size]; - } - }; - - public LibIConfig interfaceConfig; - public boolean gameRunning; - public long gameId; - public String gameTitle; - public Uri gameDirUri; - public Uri gameFileUri; - public String mainDesc; - public String varsDesc; - public List actionsList = new ArrayList<>(); - public List objectsList = new ArrayList<>(); - public List menuItemsList = new ArrayList<>(); - - public LibGameState() { - interfaceConfig = new LibIConfig(); - mainDesc = ""; - varsDesc = ""; + constructor(source: Parcel) : this ( + source.readParcelable(LibIConfig::class.java.classLoader) ?: LibIConfig(), + source.readByte().toInt() != 0, + source.readLong(), + source.readString() ?: "", + source.readParcelable(Uri::class.java.classLoader) ?: Uri.EMPTY, + source.readParcelable(Uri::class.java.classLoader) ?: Uri.EMPTY, + source.readString() ?: "", + source.readString() ?: "", + ) { + source.readTypedList(actionsList, LibListItem) + source.readTypedList(objectsList, LibListItem) + source.readTypedList(menuItemsList, LibMenuItem) } - protected LibGameState(Parcel in) { - interfaceConfig = in.readParcelable(LibIConfig.class.getClassLoader()); - gameRunning = in.readByte() != 0; - gameId = in.readLong(); - gameTitle = in.readString(); - gameDirUri = in.readParcelable(Uri.class.getClassLoader()); - gameFileUri = in.readParcelable(Uri.class.getClassLoader()); - mainDesc = in.readString(); - varsDesc = in.readString(); - in.readTypedList(actionsList, LibListItem.CREATOR); - in.readTypedList(objectsList, LibListItem.CREATOR); - in.readTypedList(menuItemsList, LibMenuItem.CREATOR); + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeParcelable(interfaceConfig, flags) + dest.writeByte((if (gameRunning) 1 else 0).toByte()) + dest.writeLong(gameId) + dest.writeString(gameTitle) + dest.writeParcelable(gameDirUri, flags) + dest.writeParcelable(gameFileUri, flags) + dest.writeString(mainDesc) + dest.writeString(varsDesc) + dest.writeTypedList(actionsList) + dest.writeTypedList(objectsList) + dest.writeTypedList(menuItemsList) } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(interfaceConfig, flags); - dest.writeByte((byte) (gameRunning ? 1 : 0)); - dest.writeLong(gameId); - dest.writeString(gameTitle); - dest.writeParcelable(gameDirUri, flags); - dest.writeParcelable(gameFileUri, flags); - dest.writeString(mainDesc); - dest.writeString(varsDesc); - dest.writeTypedList(actionsList); - dest.writeTypedList(objectsList); - dest.writeTypedList(menuItemsList); - } + override fun describeContents(): Int = 0 - @Override - public int describeContents() { - return 0; + fun reset() { + interfaceConfig.reset() + gameRunning = false + gameId = 0L + gameTitle = "" + gameDirUri = Uri.EMPTY + gameFileUri = Uri.EMPTY + mainDesc = "" + varsDesc = "" + actionsList.clear() + objectsList.clear() + menuItemsList.clear() } - public void reset() { - interfaceConfig.reset(); - gameRunning = false; - gameId = 0L; - gameTitle = ""; - gameDirUri = Uri.EMPTY; - gameFileUri = Uri.EMPTY; - mainDesc = ""; - varsDesc = ""; - actionsList = new ArrayList<>(); - objectsList = new ArrayList<>(); - menuItemsList = new ArrayList<>(); - } + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibGameState { + return LibGameState(source) + } + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt index 73e136e..a304c40 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt @@ -1,60 +1,49 @@ -package org.qp.android.questopiabundle.lib; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public class LibIConfig implements Parcelable { - - public static final Creator CREATOR = new Creator<>() { - @Override - public LibIConfig createFromParcel(Parcel in) { - return new LibIConfig(in); - } - - @Override - public LibIConfig[] newArray(int size) { - return new LibIConfig[size]; - } - }; - - public boolean useHtml; - public long fontSize; - public long backColor; - public long fontColor; - public long linkColor; - - public LibIConfig() { +package org.qp.android.questopiabundle.lib + +import android.os.Parcel +import android.os.Parcelable + +data class LibIConfig( + var useHtml: Boolean = false, + var fontSize: Long = 0L, + var backColor: Long = 0L, + var fontColor: Long = 0L, + var linkColor: Long = 0L +) : Parcelable { + + constructor(source: Parcel) : this( + source.readInt() != 0, + source.readLong(), + source.readLong(), + source.readLong(), + source.readLong() + ) + + fun reset() { + useHtml = false + fontSize = 0L + backColor = 0L + fontColor = 0L + linkColor = 0L } - protected LibIConfig(Parcel in) { - useHtml = in.readByte() != 0; - fontSize = in.readLong(); - backColor = in.readLong(); - fontColor = in.readLong(); - linkColor = in.readLong(); - } + override fun describeContents(): Int = 0 - public void reset() { - useHtml = false; - fontSize = 0; - backColor = 0; - fontColor = 0; - linkColor = 0; + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(if (useHtml) 1 else 0) + dest.writeLong(fontSize) + dest.writeLong(backColor) + dest.writeLong(fontColor) + dest.writeLong(linkColor) } - @Override - public int describeContents() { - return 0; - } + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibIConfig { + return LibIConfig(source) + } - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeByte((byte) (useHtml ? 1 : 0)); - dest.writeLong(fontSize); - dest.writeLong(backColor); - dest.writeLong(fontColor); - dest.writeLong(linkColor); + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt index 97d4ef2..573988d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt @@ -1,50 +1,34 @@ -package org.qp.android.questopiabundle.lib; +package org.qp.android.questopiabundle.lib -import android.net.Uri; +import android.net.Uri +import org.qp.android.questopiabundle.GameInterface -import org.qp.android.questopiabundle.GameInterface; - -public interface LibIProxy { +interface LibIProxy { /** * Starts the library thread. */ - void startLibThread(); - + fun startLibThread() /** * Stops the library thread. */ - void stopLibThread(); - - void enableDebugMode(boolean isDebug); - - void runGame(long gameId, String gameTitle, Uri gameDirUri, Uri gameFileUri); - - void restartGame(); - - void loadGameState(Uri uri); - - void saveGameState(Uri uri); - - void onActionClicked(int index); - - void onObjectSelected(int index); - - void onInputAreaClicked(); - - void onUseExecutorString(); - + fun stopLibThread() + fun enableDebugMode(isDebug: Boolean) + fun runGame(gameId: Long, gameTitle: String, gameDirUri: Uri, gameFileUri: Uri) + fun restartGame() + fun loadGameState(uri: Uri) + fun saveGameState(uri: Uri) + fun onActionClicked(index: Int) + fun onObjectSelected(index: Int) + fun onInputAreaClicked() + fun onUseExecutorString() /** * Starts execution of the specified line of code in the library. */ - void execute(String code); - + fun execute(code: String?) /** * Starts processing the location counter in the library. */ - void executeCounter(); - - LibGameState getGameState(); - - void setGameInterface(GameInterface view); - + fun executeCounter() + val gameState: LibGameState + fun setGameInterface(inter: GameInterface) } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt index 21d7ae9..33371ec 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt @@ -1,57 +1,41 @@ -package org.qp.android.questopiabundle.lib; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public class LibRefIRequest implements Parcelable { +package org.qp.android.questopiabundle.lib + +import android.os.Parcel +import android.os.Parcelable + +data class LibRefIRequest( + var isIConfigChanged: Boolean = false, + var isMainDescChanged: Boolean = false, + var isActionsChanged: Boolean = false, + var isObjectsChanged: Boolean = false, + var isVarsDescChanged: Boolean = false +) : Parcelable { + + constructor(source: Parcel) : this( + source.readInt() != 0, + source.readInt() != 0, + source.readInt() != 0, + source.readInt() != 0, + source.readInt() != 0, + ) + + override fun describeContents(): Int = 0 + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(if (isIConfigChanged) 1 else 0) + dest.writeInt(if (isMainDescChanged) 1 else 0) + dest.writeInt(if (isActionsChanged) 1 else 0) + dest.writeInt(if (isObjectsChanged) 1 else 0) + dest.writeInt(if (isVarsDescChanged) 1 else 0) + } - public static final Creator CREATOR = new Creator<>() { - @Override - public LibRefIRequest createFromParcel(Parcel in) { - return new LibRefIRequest(in); + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibRefIRequest { + return LibRefIRequest(source) } - @Override - public LibRefIRequest[] newArray(int size) { - return new LibRefIRequest[size]; + override fun newArray(size: Int): Array { + return arrayOfNulls(size) } - }; - - public boolean isIConfigChanged; - public boolean isMainDescChanged; - public boolean isActionsChanged; - public boolean isObjectsChanged; - public boolean isVarsDescChanged; - - public LibRefIRequest() { - isIConfigChanged = false; - isMainDescChanged = false; - isActionsChanged = false; - isObjectsChanged = false; - isVarsDescChanged = false; - } - - protected LibRefIRequest(Parcel in) { - isIConfigChanged = in.readByte() != 0; - isMainDescChanged = in.readByte() != 0; - isActionsChanged = in.readByte() != 0; - isObjectsChanged = in.readByte() != 0; - isVarsDescChanged = in.readByte() != 0; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeByte((byte) (isIConfigChanged ? 1 : 0)); - dest.writeByte((byte) (isMainDescChanged ? 1 : 0)); - dest.writeByte((byte) (isActionsChanged ? 1 : 0)); - dest.writeByte((byte) (isObjectsChanged ? 1 : 0)); - dest.writeByte((byte) (isVarsDescChanged ? 1 : 0)); } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.kt index 1bc3bd0..7f84b3e 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.kt @@ -1,11 +1,9 @@ -package org.qp.android.questopiabundle.lib; +package org.qp.android.questopiabundle.lib -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -import androidx.annotation.NonNull; - -public enum LibTypeDialog implements Parcelable { +enum class LibTypeDialog : Parcelable { DIALOG_ERROR, DIALOG_PICTURE, DIALOG_MESSAGE, @@ -16,26 +14,19 @@ public enum LibTypeDialog implements Parcelable { DIALOG_POPUP_LOAD, ; + override fun describeContents(): Int = 0 - public static final Creator CREATOR = new Creator<>() { - @Override - public LibTypeDialog createFromParcel(Parcel in) { - return LibTypeDialog.valueOf(in.readString()); - } + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(name) + } - @Override - public LibTypeDialog[] newArray(int size) { - return new LibTypeDialog[size]; + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibTypeDialog { + return valueOf(source.readString()!!) } - }; - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name()); + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.kt index 14cf717..52cc83f 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.kt @@ -1,36 +1,28 @@ -package org.qp.android.questopiabundle.lib; +package org.qp.android.questopiabundle.lib -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -import androidx.annotation.NonNull; - -public enum LibTypeWindow implements Parcelable { +enum class LibTypeWindow : Parcelable { ACTIONS, OBJECTS, VARIABLES, INPUT ; - public static final Creator CREATOR = new Creator<>() { - @Override - public LibTypeWindow createFromParcel(Parcel in) { - return LibTypeWindow.valueOf(in.readString()); - } - - @Override - public LibTypeWindow[] newArray(int size) { - return new LibTypeWindow[size]; - } - }; + override fun describeContents(): Int = 0 - @Override - public int describeContents() { - return 0; + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(name) } - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name()); + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibTypeWindow { + return valueOf(source.readString()!!) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index bc294aa..3c65b4d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -1,605 +1,523 @@ -package org.qp.android.questopiabundle.lib.impl; - -import static org.qp.android.questopiabundle.utils.FileUtil.findOrCreateFile; -import static org.qp.android.questopiabundle.utils.FileUtil.fromFullPath; -import static org.qp.android.questopiabundle.utils.FileUtil.fromRelPath; -import static org.qp.android.questopiabundle.utils.FileUtil.getFileContents; -import static org.qp.android.questopiabundle.utils.FileUtil.isWritableFile; -import static org.qp.android.questopiabundle.utils.FileUtil.writeFileContents; -import static org.qp.android.questopiabundle.utils.HtmlUtil.getSrcDir; -import static org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags; -import static org.qp.android.questopiabundle.utils.HtmlUtil.removeHtmlTags; -import static org.qp.android.questopiabundle.utils.PathUtil.getFilename; -import static org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath; -import static org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty; -import static org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank; -import static org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread; - -import android.content.Context; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.documentfile.provider.DocumentFile; - -import com.anggrayudi.storage.file.DocumentFileCompat; -import com.anggrayudi.storage.file.MimeType; -import com.libqsp.jni.QSPLib; - -import org.qp.android.questopiabundle.GameInterface; -import org.qp.android.questopiabundle.dto.LibListItem; -import org.qp.android.questopiabundle.lib.LibGameState; -import org.qp.android.questopiabundle.lib.LibIProxy; -import org.qp.android.questopiabundle.lib.LibRefIRequest; -import org.qp.android.questopiabundle.lib.LibTypeDialog; -import org.qp.android.questopiabundle.lib.LibTypeWindow; - -import java.io.File; -import java.util.ArrayList; -import java.util.Locale; -import java.util.Objects; -import java.util.concurrent.locks.ReentrantLock; - -public class LibAlphaProxyImpl extends QSPLib implements LibIProxy { - private final String TAG = "LibProxyImpl"; - - private final ReentrantLock libLock = new ReentrantLock(); - private final LibGameState gameState = new LibGameState(); - private final Context context; - private Thread libThread; - private volatile Handler libHandler; - private volatile boolean libThreadInit; - private volatile long gameStartTime; - private volatile long lastMsCountCallTime; - private GameInterface gameInterface; - - public LibAlphaProxyImpl(Context context) { - this.context = context; - } - - private DocumentFile getCurGameDir() { - return DocumentFileCompat.fromUri(context, gameState.gameDirUri); - } - - private synchronized void runOnQspThread(final Runnable runnable) { - if (libThread == null) { - Log.w(TAG, "Lib thread has not been started!"); - return; - } +package org.qp.android.questopiabundle.lib.impl + +import android.content.Context +import android.net.Uri +import android.os.Handler +import android.os.Looper +import android.os.SystemClock +import android.util.Log +import androidx.documentfile.provider.DocumentFile +import com.anggrayudi.storage.file.DocumentFileCompat.fromUri +import com.libqsp.jni.QSPLib +import org.qp.android.questopiabundle.GameInterface +import org.qp.android.questopiabundle.dto.LibListItem +import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.lib.LibIProxy +import org.qp.android.questopiabundle.lib.LibRefIRequest +import org.qp.android.questopiabundle.lib.LibTypeDialog +import org.qp.android.questopiabundle.lib.LibTypeWindow +import org.qp.android.questopiabundle.utils.FileUtil.fromFullPath +import org.qp.android.questopiabundle.utils.FileUtil.fromRelPath +import org.qp.android.questopiabundle.utils.FileUtil.getFileContents +import org.qp.android.questopiabundle.utils.FileUtil.isWritableFile +import org.qp.android.questopiabundle.utils.FileUtil.writeFileContents +import org.qp.android.questopiabundle.utils.HtmlUtil.getSrcDir +import org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags +import org.qp.android.questopiabundle.utils.HtmlUtil.removeHtmlTags +import org.qp.android.questopiabundle.utils.PathUtil.getFilename +import org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath +import org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty +import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank +import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread +import java.util.* +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.Volatile + +class LibAlphaProxyImpl( + private val context: Context, + override val gameState: LibGameState = LibGameState() +) : QSPLib(), LibIProxy { + + private val TAG = javaClass.simpleName + private val libLock = ReentrantLock() + private lateinit var libThread: Thread + @Volatile private lateinit var libHandler: Handler + @Volatile private var libThreadInit = false + @Volatile private var gameStartTime: Long = 0L + @Volatile private var lastMsCountCallTime: Long = 0L + private lateinit var gameInterface: GameInterface + private val currGameDir: DocumentFile? + get() = fromUri(context, gameState.gameDirUri) + + @Synchronized + private fun runOnQspThread(runnable: Runnable) { if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!"); - return; + Log.w(TAG, "Lib thread has been started, but not initialized!") + return } - var mLibHandler = libHandler; - if (mLibHandler == null) return; - mLibHandler.post(() -> { - libLock.lock(); + val mLibHandler = libHandler + mLibHandler.post { + libLock.lock() try { - runnable.run(); + runnable.run() } finally { - libLock.unlock(); + libLock.unlock() } - }); + } } - private boolean loadGameWorld() { - var gameFileUri = gameState.gameFileUri; - var gameData = getFileContents(context, gameFileUri); - if (gameData == null) return false; + private fun loadGameWorld(): Boolean { + val gameFileUri = gameState.gameFileUri + val gameData = getFileContents(context, gameFileUri) ?: return false if (!loadGameWorldFromData(gameData, true)) { - showLastQspError(); - Log.d("QSP", "World is not loaded!"); - return false; + showLastQspError() + return false } - Log.d("QSP", "World is loaded!"); - return true; + return true } - private void showLastQspError() { - var errorData = getLastErrorData(); - var locName = getStringOrEmpty(errorData.locName); - var desc = getStringOrEmpty(getErrorDesc(errorData.errorNum)); - final var message = String.format( - Locale.getDefault(), - "Location: %s\nAction: %d\nLine: %d\nError number: %d\nDescription: %s", - locName, - errorData.actIndex, - errorData.intLineNum, - errorData.errorNum, - desc); - Log.e(TAG, errorData.toString()); - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, message); - } + private fun showLastQspError() { + val errorData = lastErrorData + val locName = getStringOrEmpty(errorData.locName) + val desc = getStringOrEmpty(getErrorDesc(errorData.errorNum)) + val message = String.format( + Locale.getDefault(), + "Location: %s\nAction: %d\nLine: %d\nError number: %d\nDescription: %s", + locName, + errorData.actIndex, + errorData.intLineNum, + errorData.errorNum, + desc + ) + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, message) } /** * Loads the interface configuration - using HTML, font and colors - from the library. * - * @return true if the configuration has changed, otherwise false + * @return `true` if the configuration has changed, otherwise `false` */ - private boolean loadInterfaceConfiguration() { - var config = gameState.interfaceConfig; - boolean changed = false; + private fun loadInterfaceConfiguration(): Boolean { + val config = gameState.interfaceConfig + var changed = false - var htmlResult = getNumVarValue("USEHTML", 0); - var useHtml = htmlResult != 0; + val htmlResult = getNumVarValue("USEHTML", 0) + val useHtml = htmlResult != 0L if (config.useHtml != useHtml) { - config.useHtml = useHtml; - changed = true; + config.useHtml = useHtml + changed = true } - var fSizeResult = getNumVarValue("FSIZE", 0); + val fSizeResult = getNumVarValue("FSIZE", 0) if (config.fontSize != fSizeResult) { - config.fontSize = fSizeResult; - changed = true; + config.fontSize = fSizeResult + changed = true } - var bColorResult = getNumVarValue("BCOLOR", 0); + val bColorResult = getNumVarValue("BCOLOR", 0) if (config.backColor != bColorResult) { - config.backColor = bColorResult; - changed = true; + config.backColor = bColorResult + changed = true } - var fColorResult = getNumVarValue("FCOLOR", 0); + val fColorResult = getNumVarValue("FCOLOR", 0) if (config.fontColor != fColorResult) { - config.fontColor = fColorResult; - changed = true; + config.fontColor = fColorResult + changed = true } - var lColorResult = getNumVarValue("LCOLOR", 0); + val lColorResult = getNumVarValue("LCOLOR", 0) if (config.linkColor != lColorResult) { - config.linkColor = lColorResult; - changed = true; + config.linkColor = lColorResult + changed = true } - return changed; - } - - @NonNull - private ArrayList getActionsList() { - var actions = new ArrayList(); - var curGameDir = getCurGameDir(); - - for (var element : getActions()) { - var newElement = new LibListItem(element); - if (isNotEmptyOrBlank(newElement.pathToImage)) { - var tempPath = normalizeContentPath(getFilename(newElement.pathToImage)); - var fileFromPath = fromRelPath(context, tempPath, curGameDir, false); - if (fileFromPath != null) { - newElement.pathToImage = String.valueOf(fileFromPath.getUri()); + return changed + } + + private val actionsList: ArrayList + get() { + val actions = ArrayList() + val currGameDir = currGameDir + + for (element in getActions()) { + val newElement = LibListItem(element) + if (isNotEmptyOrBlank(newElement.pathToImage) && currGameDir != null) { + val tempPath = + normalizeContentPath(getFilename(newElement.pathToImage)) + val fileFromPath = + fromRelPath(context, tempPath, currGameDir, false) + if (fileFromPath != null) { + newElement.pathToImage = fileFromPath.uri.toString() + } else { + newElement.pathToImage = "" + } + } + newElement.text = if (gameState.interfaceConfig.useHtml) { + removeHtmlTags(newElement.text) } else { - newElement.pathToImage = null; + newElement.text } + actions.add(newElement) } - newElement.text = gameState.interfaceConfig.useHtml - ? removeHtmlTags(newElement.text) - : newElement.text; - actions.add(newElement); - } - return actions; - } - - @NonNull - private ArrayList getObjectsList() { - var objects = new ArrayList(); - var curGameDir = getCurGameDir(); + return actions + } - for (var element : getObjects()) { - var newElement = new LibListItem(element); - if (newElement.text.contains(" + get() { + val objects = ArrayList() + val currGameDir = currGameDir + + for (element in getObjects()) { + val newElement = LibListItem(element) + if (newElement.text.contains(" { - while (!Thread.currentThread().isInterrupted()) { + override fun startLibThread() { + libThread = Thread({ + while (!Thread.currentThread().isInterrupted) { try { - init(); + init() if (Looper.myLooper() == null) { - Looper.prepare(); + Looper.prepare() } - libHandler = new Handler(Looper.myLooper()); - libThreadInit = true; - Looper.loop(); - terminate(); - } catch (Throwable t) { - Log.e(TAG, "lib thread has stopped exceptionally", t); - Thread.currentThread().interrupt(); + libHandler = Handler(Looper.myLooper()!!) + libThreadInit = true + Looper.loop() + terminate() + } catch (t: Throwable) { + Log.e(TAG, "lib thread has stopped exceptionally", t) + Thread.currentThread().interrupt() } } - }, "libQSP"); - libThread.start(); + }, "libQSP") + libThread.start() } - public synchronized void stopLibThread() { - if (libThread == null) return; + @Synchronized + override fun stopLibThread() { if (libThreadInit) { - var handler = libHandler; - if (handler != null) { - handler.getLooper().quitSafely(); - } - libThreadInit = false; + val handler = libHandler + handler.looper.quitSafely() + libThreadInit = false } else { - Log.w(TAG, "lib thread has been started, but not initialized"); + Log.w(TAG, "lib thread has been started, but not initialized") } - libThread.interrupt(); + libThread.interrupt() } - public void enableDebugMode(boolean isDebug) { - runOnQspThread(() -> enableDebugMode(isDebug)); + override fun enableDebugMode(isDebug: Boolean) { + runOnQspThread { enableDebugMode(isDebug) } } - @Override - public void runGame(long gameId, - String gameTitle, - Uri gameDirUri, - Uri gameFileUri) { - runOnQspThread(() -> doRunGame(gameId, gameTitle, gameDirUri, gameFileUri)); + override fun runGame( + gameId: Long, + gameTitle: String, + gameDirUri: Uri, + gameFileUri: Uri + ) { + runOnQspThread { doRunGame(gameId, gameTitle, gameDirUri, gameFileUri) } } - @Override - public void restartGame() { - runOnQspThread(() -> doRunGame(gameState.gameId, gameState.gameTitle, gameState.gameDirUri, gameState.gameFileUri)); + override fun restartGame() { + runOnQspThread { + doRunGame( + gameState.gameId, + gameState.gameTitle, + gameState.gameDirUri, + gameState.gameFileUri + ) + } } - private void doRunGame(final long id, final String title, final Uri dir, final Uri file) { - gameInterface.doWithCounterDisabled(() -> { - gameInterface.closeAllFiles(); - gameState.reset(); - gameState.gameRunning = true; - gameState.gameId = id; - gameState.gameTitle = title; - gameState.gameDirUri = dir; - gameState.gameFileUri = file; - gameInterface.doChangeCurrGameDir(dir); - if (!loadGameWorld()) return; - gameStartTime = SystemClock.elapsedRealtime(); - lastMsCountCallTime = 0; + private fun doRunGame(id: Long, title: String, dir: Uri, file: Uri) { + gameInterface.doWithCounterDisabled { + gameInterface.closeAllFiles() + gameState.reset() + gameState.gameRunning = true + gameState.gameId = id + gameState.gameTitle = title + gameState.gameDirUri = dir + gameState.gameFileUri = file + gameInterface.doChangeCurrGameDir(dir) + if (!loadGameWorld()) return@doWithCounterDisabled + gameStartTime = SystemClock.elapsedRealtime() + lastMsCountCallTime = 0 if (!restartGame(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void loadGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> loadGameState(uri)); - return; + override fun loadGameState(uri: Uri) { + if (!isSameThread(libHandler.looper.thread)) { + runOnQspThread { loadGameState(uri) } + return } - final var gameData = getFileContents(context, uri); - if (gameData == null) return; + + gameInterface.requestPermFile(uri) + val gameData = getFileContents(context, uri) ?: return if (!openSavedGameFromData(gameData, true)) { - showLastQspError(); + showLastQspError() } } - @Override - public void saveGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> saveGameState(uri)); - return; + override fun saveGameState(uri: Uri) { + if (!isSameThread(libHandler.looper.thread)) { + runOnQspThread { saveGameState(uri) } + return } - final var gameData = saveGameAsData(false); - if (gameData == null) return; - writeFileContents(context, uri, gameData); + + gameInterface.requestPermFile(uri) + val gameData = saveGameAsData(false) ?: return + writeFileContents(context, uri, gameData) } - @Override - public void onActionClicked(final int index) { - runOnQspThread(() -> { + override fun onActionClicked(index: Int) { + runOnQspThread { if (!setSelActIndex(index, false)) { - showLastQspError(); + showLastQspError() } if (!execSelAction(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onObjectSelected(final int index) { - runOnQspThread(() -> { + override fun onObjectSelected(index: Int) { + runOnQspThread { if (!setSelObjIndex(index, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onInputAreaClicked() { - if (gameInterface == null) return; - runOnQspThread(() -> { - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, "userInputTitle"); - if (doShow == null) return; - var input = doShow.outTextValue; - setInputStrText(input); + override fun onInputAreaClicked() { + runOnQspThread { + val doShow = + gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, "userInputTitle") + ?: return@runOnQspThread + val input = doShow.outTextValue + setInputStrText(input) if (!execUserInput(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onUseExecutorString() { - if (gameInterface == null) return; - runOnQspThread(() -> { - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_EXECUTOR, "execStringTitle"); - if (doShow == null) return; - var input = doShow.outTextValue; + override fun onUseExecutorString() { + runOnQspThread { + val doShow = + gameInterface.showLibDialog(LibTypeDialog.DIALOG_EXECUTOR, "execStringTitle") + ?: return@runOnQspThread + val input = doShow.outTextValue if (!execString(input, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void execute(final String code) { - runOnQspThread(() -> { + override fun execute(code: String?) { + runOnQspThread { if (!execString(code, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void executeCounter() { - if (libLock.isLocked()) return; - runOnQspThread(() -> { + override fun executeCounter() { + if (libLock.isLocked) return + runOnQspThread { if (!execCounter(true)) { - showLastQspError(); + showLastQspError() } - }); - } - - @Override - public LibGameState getGameState() { - return gameState; + } } - @Override - public void setGameInterface(GameInterface inter) { - this.gameInterface = inter; + override fun setGameInterface(inter: GameInterface) { + this.gameInterface = inter } - // endregion LibQpProxy - - // region LibQpCallbacks - - @Override - public void onRefreshInt(boolean isForced) { - var request = new LibRefIRequest(); - + override fun onRefreshInt(isForced: Boolean) { + val request = LibRefIRequest() if (loadInterfaceConfiguration()) { - request.isIConfigChanged = true; + request.isIConfigChanged = true } - if (isMainDescChanged()) { + if (isMainDescChanged) { if (isNotEmptyOrBlank(gameState.mainDesc)) { - if (!gameState.mainDesc.equals(getMainDesc())) { - gameState.mainDesc = getMainDesc(); - request.isMainDescChanged = true; + if (gameState.mainDesc != mainDesc) { + gameState.mainDesc = mainDesc + request.isMainDescChanged = true } } else { - gameState.mainDesc = getMainDesc(); - request.isMainDescChanged = true; + gameState.mainDesc = mainDesc + request.isMainDescChanged = true } } - if (isActsChanged()) { - if (gameState.actionsList != null) { - if (gameState.actionsList != getActionsList()) { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; - } - } else { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; + if (isActsChanged) { + if (gameState.actionsList !== actionsList) { + gameState.actionsList = actionsList + request.isActionsChanged = true } } - if (isObjsChanged()) { - if (gameState.objectsList != null) { - if (gameState.objectsList != getObjectsList()) { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; - } - } else { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; + if (isObjsChanged) { + if (gameState.objectsList !== objectsList) { + gameState.objectsList = objectsList + request.isObjectsChanged = true } } - if (isVarsDescChanged()) { + if (isVarsDescChanged) { if (isNotEmptyOrBlank(gameState.varsDesc)) { - if (!gameState.varsDesc.equals(getVarsDesc())) { - gameState.varsDesc = getVarsDesc(); - request.isVarsDescChanged = true; + if (gameState.varsDesc != varsDesc) { + gameState.varsDesc = varsDesc + request.isVarsDescChanged = true } } else { - gameState.varsDesc = getVarsDesc(); - request.isVarsDescChanged = true; + gameState.varsDesc = varsDesc + request.isVarsDescChanged = true } } - var inter = gameInterface; - if (inter != null) { - inter.doRefresh(request); - } + gameInterface.doRefresh(request) } - @Override - public void onShowImage(String file) { - var inter = gameInterface; - if (inter == null) return; - if (!isNotEmptyOrBlank(file)) return; - inter.showLibDialog(LibTypeDialog.DIALOG_PICTURE, file); + override fun onShowImage(file: String) { + if (!isNotEmptyOrBlank(file)) return + gameInterface.showLibDialog(LibTypeDialog.DIALOG_PICTURE, file) } - @Override - public void onSetTimer(int msecs) { - var inter = gameInterface; - if (inter == null) return; - inter.setCountInter(msecs); + override fun onSetTimer(msecs: Int) { + gameInterface.setCountInter(msecs) } - @Override - public void onShowMessage(String text) { - var inter = gameInterface; - if (inter == null) return; - inter.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, text); + override fun onShowMessage(text: String) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, text) } - @Override - public void onPlayFile(String file, int volume) { - if (gameInterface == null) return; - if (!isNotEmptyOrBlank(file)) return; - gameInterface.playFile(file, volume); + override fun onPlayFile(file: String, volume: Int) { + if (!isNotEmptyOrBlank(file)) return + gameInterface.playFile(file, volume) } - @Override - public boolean onIsPlayingFile(String file) { - if (gameInterface == null) return false; - return isNotEmptyOrBlank(file) && gameInterface.isPlayingFile(file); + override fun onIsPlayingFile(file: String): Boolean { + return isNotEmptyOrBlank(file) && gameInterface.isPlayingFile(file) } - @Override - public void onCloseFile(String file) { - if (gameInterface == null) return; + override fun onCloseFile(file: String) { if (isNotEmptyOrBlank(file)) { - gameInterface.closeFile(file); + gameInterface.closeFile(file) } else { - gameInterface.closeAllFiles(); + gameInterface.closeAllFiles() } } - @Override - public void onOpenGameStatus(String file) { + override fun onOpenGameStatus(file: String?) { if (file == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) } else { try { - var saveFile = fromFullPath(context, file); - if (!isWritableFile(context, saveFile)) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found"); - } - Log.e(TAG, "Save file not found"); - return; - } - if (gameInterface != null) { - gameInterface.doWithCounterDisabled(() -> loadGameState(saveFile.getUri())); - } - } catch (Exception e) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()); + val saveFile = fromFullPath(context, file) ?: return + gameInterface.requestPermFile(saveFile.uri) + if (isWritableFile(context, saveFile)) { + gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } + } else { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found") + Log.e(TAG, "Save file not found") } - Log.e(TAG, "Error: ", e); + } catch (e: Exception) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()) + Log.e(TAG, "Error: ", e) } } } - @Override - public void onSaveGameStatus(String file) { + override fun onSaveGameStatus(file: String?) { if (file == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_SAVE, null); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_SAVE, null) } else { - var save = new File(file); - var saveFile = findOrCreateFile(context, getCurGameDir(), save.getName(), MimeType.TEXT); - if (isWritableFile(context, saveFile)) { - saveGameState(saveFile.getUri()); + val currGameDir = currGameDir ?: return + val saveFileUri = gameInterface.requestCreateFile(currGameDir.uri, file) + if (saveFileUri != Uri.EMPTY) { + saveGameState(saveFileUri) } else { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir"); - } - Log.e(TAG, "Error access dir"); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir") + Log.e(TAG, "Error access dir") } } } - @Override - public String onInputBox(String text) { - if (gameInterface == null) return ""; - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, text); - if (doShow == null) return ""; - return doShow.outTextValue; + override fun onInputBox(text: String): String { + val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, text) ?: return "" + return doShow.outTextValue } - @Override - public int onGetMsCount() { - var now = SystemClock.elapsedRealtime(); - if (lastMsCountCallTime == 0) { - lastMsCountCallTime = gameStartTime; + override fun onGetMsCount(): Int { + val now = SystemClock.elapsedRealtime() + if (lastMsCountCallTime == 0L) { + lastMsCountCallTime = gameStartTime } - var dt = now - lastMsCountCallTime; - lastMsCountCallTime = now; - return (int) dt; + val dt = now - lastMsCountCallTime + lastMsCountCallTime = now + return dt.toInt() } - @Override - public int onShowMenu(ListItem[] items) { - if (gameInterface == null) return super.onShowMenu(items); - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_MENU, null); - if (doShow == null) return super.onShowMenu(items); - var result = doShow.outNumValue; + override fun onShowMenu(items: Array): Int { + val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_MENU, null) + ?: return super.onShowMenu(items) + val result = doShow.outNumValue if (result != -1) { - return result; + return result } - return super.onShowMenu(items); + return super.onShowMenu(items) } - @Override - public void onSleep(int msecs) { + override fun onSleep(msecs: Int) { try { - Thread.sleep(msecs); - } catch (InterruptedException ex) { - Log.e(TAG, "Wait failed", ex); + Thread.sleep(msecs.toLong()) + } catch (ex: InterruptedException) { + Log.e(TAG, "Wait failed", ex) } } - @Override - public void onShowWindow(int type, boolean toShow) { - if (gameInterface == null) return; - var windowType = LibTypeWindow.values()[type]; - gameInterface.changeVisWindow(windowType, toShow); + override fun onShowWindow(type: Int, toShow: Boolean) { + val windowType = LibTypeWindow.entries[type] + gameInterface.changeVisWindow(windowType, toShow) } - @Override - public void onOpenGame(String file, boolean isNewGame) { - var newGameDir = fromFullPath(context, file); + override fun onOpenGame(file: String, isNewGame: Boolean) { + val newGameDir = fromFullPath(context, file) if (newGameDir == null || !newGameDir.exists()) { - Log.e(TAG, "Game directory not found: " + file); - return; + Log.e(TAG, "Game directory not found: $file") + return } - var currGameDirUri = getCurGameDir().getUri(); - var newGameDirUri = newGameDir.getUri(); - if (!Objects.equals(currGameDirUri, newGameDirUri)) { - gameState.gameDirUri = newGameDirUri; - gameInterface.doChangeCurrGameDir(newGameDirUri); + val currGameDir = currGameDir ?: return + val currGameDirUri = currGameDir.uri + val newGameDirUri = newGameDir.uri + if (currGameDirUri != newGameDirUri) { + gameState.gameDirUri = newGameDirUri + gameInterface.doChangeCurrGameDir(newGameDirUri) } } - - // endregion LibQpCallbacks } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index a6d3f1d..94a8521 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -1,638 +1,543 @@ -package org.qp.android.questopiabundle.lib.impl; - -import static org.qp.android.questopiabundle.utils.FileUtil.documentWrap; -import static org.qp.android.questopiabundle.utils.FileUtil.findOrCreateFile; -import static org.qp.android.questopiabundle.utils.FileUtil.fromFullPath; -import static org.qp.android.questopiabundle.utils.FileUtil.fromRelPath; -import static org.qp.android.questopiabundle.utils.FileUtil.getFileContents; -import static org.qp.android.questopiabundle.utils.FileUtil.isWritableFile; -import static org.qp.android.questopiabundle.utils.FileUtil.writeFileContents; -import static org.qp.android.questopiabundle.utils.HtmlUtil.getSrcDir; -import static org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags; -import static org.qp.android.questopiabundle.utils.HtmlUtil.removeHtmlTags; -import static org.qp.android.questopiabundle.utils.PathUtil.getFilename; -import static org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath; -import static org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty; -import static org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank; -import static org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread; - -import android.content.Context; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.documentfile.provider.DocumentFile; - -import com.anggrayudi.storage.file.DocumentFileCompat; -import com.anggrayudi.storage.file.MimeType; - -import org.qp.android.questopiabundle.GameInterface; -import org.qp.android.questopiabundle.dto.LibListItem; -import org.qp.android.questopiabundle.dto.LibMenuItem; -import org.qp.android.questopiabundle.lib.LibGameState; -import org.qp.android.questopiabundle.lib.LibIProxy; -import org.qp.android.questopiabundle.lib.LibRefIRequest; -import org.qp.android.questopiabundle.lib.LibTypeDialog; -import org.qp.android.questopiabundle.lib.LibTypeWindow; -import org.qp.android.questopiabundle.libbravo.NDKLib; - -import java.io.File; -import java.util.ArrayList; -import java.util.Locale; -import java.util.Objects; -import java.util.concurrent.locks.ReentrantLock; - -public class LibBravoProxyImpl extends NDKLib implements LibIProxy { - private final String TAG = "LibProxyImpl"; - - private final ReentrantLock libLock = new ReentrantLock(); - private final LibGameState gameState = new LibGameState(); - private final Context context; - private Thread libThread; - private volatile Handler libHandler; - private volatile boolean libThreadInit; - private volatile long gameStartTime; - private volatile long lastMsCountCallTime; - private GameInterface gameInterface; - - public LibBravoProxyImpl(Context context) { - this.context = context; - } - - private DocumentFile getCurGameDir() { - return DocumentFileCompat.fromUri(context, gameState.gameDirUri); - } - - private synchronized void runOnQspThread(final Runnable runnable) { - if (libThread == null) { - Log.w(TAG, "Lib thread has not been started!"); - return; - } +package org.qp.android.questopiabundle.lib.impl + +import android.content.Context +import android.net.Uri +import android.os.Handler +import android.os.Looper +import android.os.SystemClock +import android.util.Log +import androidx.documentfile.provider.DocumentFile +import com.anggrayudi.storage.file.DocumentFileCompat.fromUri +import org.libndkqsp.jni.NDKLib +import org.qp.android.questopiabundle.GameInterface +import org.qp.android.questopiabundle.dto.LibListItem +import org.qp.android.questopiabundle.dto.LibMenuItem +import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.lib.LibIProxy +import org.qp.android.questopiabundle.lib.LibRefIRequest +import org.qp.android.questopiabundle.lib.LibTypeDialog +import org.qp.android.questopiabundle.lib.LibTypeWindow +import org.qp.android.questopiabundle.utils.FileUtil.documentWrap +import org.qp.android.questopiabundle.utils.FileUtil.fromFullPath +import org.qp.android.questopiabundle.utils.FileUtil.fromRelPath +import org.qp.android.questopiabundle.utils.FileUtil.getFileContents +import org.qp.android.questopiabundle.utils.FileUtil.isWritableFile +import org.qp.android.questopiabundle.utils.FileUtil.writeFileContents +import org.qp.android.questopiabundle.utils.HtmlUtil.getSrcDir +import org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags +import org.qp.android.questopiabundle.utils.HtmlUtil.removeHtmlTags +import org.qp.android.questopiabundle.utils.PathUtil.getFilename +import org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath +import org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty +import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank +import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread +import java.util.* +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.Volatile + +class LibBravoProxyImpl( + private val context: Context, + override val gameState: LibGameState = LibGameState() +) : NDKLib(), LibIProxy { + + private val TAG = javaClass.simpleName + private val libLock = ReentrantLock() + private lateinit var libThread: Thread + @Volatile private lateinit var libHandler: Handler + @Volatile private var libThreadInit = false + @Volatile private var gameStartTime: Long = 0L + @Volatile private var lastMsCountCallTime: Long = 0L + private lateinit var gameInterface: GameInterface + private val currGameDir: DocumentFile? + get() = fromUri(context, gameState.gameDirUri) + + @Synchronized + private fun runOnQspThread(runnable: Runnable) { if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!"); - return; + Log.w(TAG, "Lib thread has been started, but not initialized!") + return } - var mLibHandler = libHandler; - if (mLibHandler == null) return; - mLibHandler.post(() -> { - libLock.lock(); + val mLibHandler = libHandler + mLibHandler.post { + libLock.lock() try { - runnable.run(); + runnable.run() } finally { - libLock.unlock(); + libLock.unlock() } - }); - } - - private boolean loadGameWorld() { - var gameFileUri = gameState.gameFileUri; - var gameFile = DocumentFileCompat.fromUri(context, gameState.gameFileUri); - if (gameFile == null) return false; - var gameFileFullPath = documentWrap(gameFile).getAbsolutePath(context); - var gameData = getFileContents(context, gameFileUri); - if (gameData == null) return false; - - if (!QSPLoadGameWorldFromData(gameData, gameData.length, gameFileFullPath)) { - showLastQspError(); - Log.d("QSP", "World is not loaded!"); - return false; } - Log.d("QSP", "World is loaded!"); - return true; } - private void showLastQspError() { - var errorData = (ErrorData) QSPGetLastErrorData(); - var locName = getStringOrEmpty(errorData.locName()); - var desc = getStringOrEmpty(QSPGetErrorDesc(errorData.errorNum())); - final var message = String.format( - Locale.getDefault(), - "Location: %s\nAction: %d\nLine: %d\nError number: %d\nDescription: %s", - locName, - errorData.index(), - errorData.line(), - errorData.errorNum(), - desc); - Log.e(TAG, errorData.toString()); - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, message); + private fun loadGameWorld(): Boolean { + val gameFileUri = gameState.gameFileUri + val gameFile = fromUri( + context, + gameState.gameFileUri + ) + if (gameFile == null) return false + val gameFileFullPath = documentWrap(gameFile).getAbsolutePath(context) + val gameData = getFileContents(context, gameFileUri) ?: return false + + if (!QSPLoadGameWorldFromData(gameData, gameFileFullPath)) { + showLastQspError() + Log.d("QSP", "World is not loaded!") + return false } + Log.d("QSP", "World is loaded!") + return true + } + + private fun showLastQspError() { + val errorData = QSPGetLastErrorData() as ErrorData + val locName = getStringOrEmpty(errorData.locName) + val desc = getStringOrEmpty(QSPGetErrorDesc(errorData.errorNum)) + val message = String.format( + Locale.getDefault(), + "Location: %s\nAction: %d\nLine: %d\nError number: %d\nDescription: %s", + locName, + errorData.index, + errorData.line, + errorData.errorNum, + desc + ) + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, message) } /** * Loads the interface configuration - using HTML, font and colors - from the library. * - * @return true if the configuration has changed, otherwise false + * @return `true` if the configuration has changed, otherwise `false` */ - private boolean loadInterfaceConfiguration() { - var config = gameState.interfaceConfig; - boolean changed = false; + private fun loadInterfaceConfiguration(): Boolean { + val config = gameState.interfaceConfig + var changed = false - var htmlResult = (VarValResp) QSPGetVarValues("USEHTML", 0); - if (htmlResult.isSuccess()) { - boolean useHtml = htmlResult.intValue() != 0; + val htmlResult = QSPGetVarValues("USEHTML", 0) as VarValResp + if (htmlResult.isSuccess) { + val useHtml = htmlResult.intValue != 0 if (config.useHtml != useHtml) { - config.useHtml = useHtml; - changed = true; + config.useHtml = useHtml + changed = true } } - var fSizeResult = (VarValResp) QSPGetVarValues("FSIZE", 0); - if (fSizeResult.isSuccess() && config.fontSize != fSizeResult.intValue()) { - config.fontSize = fSizeResult.intValue(); - changed = true; + val fSizeResult = QSPGetVarValues("FSIZE", 0) as VarValResp + if (fSizeResult.isSuccess && config.fontSize != fSizeResult.intValue.toLong()) { + config.fontSize = fSizeResult.intValue.toLong() + changed = true } - var bColorResult = (VarValResp) QSPGetVarValues("BCOLOR", 0); - if (bColorResult.isSuccess() && config.backColor != bColorResult.intValue()) { - config.backColor = bColorResult.intValue(); - changed = true; + val bColorResult = QSPGetVarValues("BCOLOR", 0) as VarValResp + if (bColorResult.isSuccess && config.backColor != bColorResult.intValue.toLong()) { + config.backColor = bColorResult.intValue.toLong() + changed = true } - var fColorResult = (VarValResp) QSPGetVarValues("FCOLOR", 0); - if (fColorResult.isSuccess() && config.fontColor != fColorResult.intValue()) { - config.fontColor = fColorResult.intValue(); - changed = true; + val fColorResult = QSPGetVarValues("FCOLOR", 0) as VarValResp + if (fColorResult.isSuccess && config.fontColor != fColorResult.intValue.toLong()) { + config.fontColor = fColorResult.intValue.toLong() + changed = true } - var lColorResult = (VarValResp) QSPGetVarValues("LCOLOR", 0); - if (lColorResult.isSuccess() && config.linkColor != lColorResult.intValue()) { - config.linkColor = lColorResult.intValue(); - changed = true; + val lColorResult = QSPGetVarValues("LCOLOR", 0) as VarValResp + if (lColorResult.isSuccess && config.linkColor != lColorResult.intValue.toLong()) { + config.linkColor = lColorResult.intValue.toLong() + changed = true } - return changed; - } - - @NonNull - private ArrayList getActionsList() { - var actions = new ArrayList(); - var count = QSPGetActionsCount(); - - for (int i = 0; i < count; ++i) { - var action = new LibListItem(); - var actionResult = (NDKLib.ListItem) QSPGetActionData(i); - var curGameDir = getCurGameDir(); - - if (actionResult.pathToImage == null) { - action.pathToImage = null; - } else { - var tempPath = normalizeContentPath(getFilename(actionResult.pathToImage)); - var fileFromPath = fromRelPath(context, tempPath, curGameDir, false); - if (fileFromPath != null) { - action.pathToImage = String.valueOf(fileFromPath.getUri()); + return changed + } + + private val actionsList: ArrayList + get() { + val actions = ArrayList() + val currGameDir = currGameDir + + for (element in QSPGetActionData()) { + val newElement = LibListItem(element) + if (isNotEmptyOrBlank(newElement.pathToImage) && currGameDir != null) { + val tempPath = + normalizeContentPath(getFilename(newElement.pathToImage)) + val fileFromPath = + fromRelPath(context, tempPath, currGameDir, false) + if (fileFromPath != null) { + newElement.pathToImage = fileFromPath.uri.toString() + } else { + newElement.pathToImage = "" + } + } + newElement.text = if (gameState.interfaceConfig.useHtml) { + removeHtmlTags(newElement.text) } else { - action.pathToImage = null; + newElement.text } + actions.add(newElement) } - action.text = gameState.interfaceConfig.useHtml - ? removeHtmlTags(actionResult.text) - : actionResult.text; - actions.add(action); + return actions } - return actions; - } - - @NonNull - private ArrayList getObjectsList() { - var objects = new ArrayList(); - var count = QSPGetObjectsCount(); - - for (int i = 0; i < count; i++) { - var object = new LibListItem(); - var objectResult = (NDKLib.ListItem) QSPGetObjectData(i); - var curGameDir = getCurGameDir(); - - if (objectResult.text.contains(" + get() { + val objects = ArrayList() + val currGameDir = currGameDir + + for (element in QSPGetObjectData()) { + val newElement = LibListItem(element) + if (newElement.text.contains(" { - while (!Thread.currentThread().isInterrupted()) { + override fun startLibThread() { + libThread = Thread({ + while (!Thread.currentThread().isInterrupted) { try { - QSPInit(); + QSPInit() if (Looper.myLooper() == null) { - Looper.prepare(); + Looper.prepare() } - libHandler = new Handler(Looper.myLooper()); - libThreadInit = true; - Looper.loop(); - QSPDeInit(); - } catch (Throwable t) { - Log.e(TAG, "lib thread has stopped exceptionally", t); - Thread.currentThread().interrupt(); + libHandler = Handler(Looper.myLooper()!!) + libThreadInit = true + Looper.loop() + QSPDeInit() + } catch (t: Throwable) { + Log.e(TAG, "lib thread has stopped exceptionally", t) + Thread.currentThread().interrupt() } } - }, "libNDKQSP"); - libThread.start(); + }, "libNDKQSP") + libThread.start() } - public synchronized void stopLibThread() { - if (libThread == null) return; + @Synchronized + override fun stopLibThread() { if (libThreadInit) { - var handler = libHandler; - if (handler != null) { - handler.getLooper().quitSafely(); - } - libThreadInit = false; + val handler = libHandler + handler.looper.quitSafely() + libThreadInit = false } else { - Log.w(TAG, "lib thread has been started, but not initialized"); + Log.w(TAG, "lib thread has been started, but not initialized") } - libThread.interrupt(); + libThread.interrupt() } - public void enableDebugMode(boolean isDebug) { - runOnQspThread(() -> enableDebugMode(isDebug)); + override fun enableDebugMode(isDebug: Boolean) { + runOnQspThread { enableDebugMode(isDebug) } } - @Override - public void runGame(long gameId, - String gameTitle, - Uri gameDirUri, - Uri gameFileUri) { - runOnQspThread(() -> doRunGame(gameId, gameTitle, gameDirUri, gameFileUri)); + override fun runGame( + gameId: Long, + gameTitle: String, + gameDirUri: Uri, + gameFileUri: Uri + ) { + runOnQspThread { doRunGame(gameId, gameTitle, gameDirUri, gameFileUri) } } - @Override - public void restartGame() { - runOnQspThread(() -> doRunGame(gameState.gameId, gameState.gameTitle, gameState.gameDirUri, gameState.gameFileUri)); + override fun restartGame() { + runOnQspThread { + doRunGame( + gameState.gameId, + gameState.gameTitle, + gameState.gameDirUri, + gameState.gameFileUri + ) + } } - private void doRunGame(final long id, final String title, final Uri dir, final Uri file) { - gameInterface.doWithCounterDisabled(() -> { - gameInterface.closeAllFiles(); - gameState.reset(); - gameState.gameRunning = true; - gameState.gameId = id; - gameState.gameTitle = title; - gameState.gameDirUri = dir; - gameState.gameFileUri = file; - gameInterface.doChangeCurrGameDir(dir); - if (!loadGameWorld()) return; - gameStartTime = SystemClock.elapsedRealtime(); - lastMsCountCallTime = 0; + private fun doRunGame(id: Long, title: String, dir: Uri, file: Uri) { + gameInterface.doWithCounterDisabled { + gameInterface.closeAllFiles() + gameState.reset() + gameState.gameRunning = true + gameState.gameId = id + gameState.gameTitle = title + gameState.gameDirUri = dir + gameState.gameFileUri = file + gameInterface.doChangeCurrGameDir(dir) + if (!loadGameWorld()) return@doWithCounterDisabled + gameStartTime = SystemClock.elapsedRealtime() + lastMsCountCallTime = 0 if (!QSPRestartGame(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void loadGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> loadGameState(uri)); - return; + override fun loadGameState(uri: Uri) { + if (!isSameThread(libHandler.looper.thread)) { + runOnQspThread { loadGameState(uri) } + return } - final var gameData = getFileContents(context, uri); - if (gameData == null) return; - if (!QSPOpenSavedGameFromData(gameData, gameData.length, true)) { - showLastQspError(); + + gameInterface.requestPermFile(uri) + val gameData = getFileContents(context, uri) ?: return + if (!QSPOpenSavedGameFromData(gameData, gameData.size, true)) { + showLastQspError() } } - @Override - public void saveGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> saveGameState(uri)); - return; + override fun saveGameState(uri: Uri) { + if (!isSameThread(libHandler.looper.thread)) { + runOnQspThread { saveGameState(uri) } + return } - final var gameData = QSPSaveGameAsData(false); - if (gameData == null) return; - writeFileContents(context, uri, gameData); + + val gameData = QSPSaveGameAsData(false) ?: return + gameInterface.requestPermFile(uri) + writeFileContents(context, uri, gameData) } - @Override - public void onActionClicked(final int index) { - runOnQspThread(() -> { + override fun onActionClicked(index: Int) { + runOnQspThread { if (!QSPSetSelActionIndex(index, false)) { - showLastQspError(); + showLastQspError() } if (!QSPExecuteSelActionCode(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onObjectSelected(final int index) { - runOnQspThread(() -> { + override fun onObjectSelected(index: Int) { + runOnQspThread { if (!QSPSetSelObjectIndex(index, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onInputAreaClicked() { - if (gameInterface == null) return; - runOnQspThread(() -> { - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, "userInputTitle"); - if (doShow == null) return; - var input = doShow.outTextValue; - QSPSetInputStrText(input); + override fun onInputAreaClicked() { + runOnQspThread { + val doShow = + gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, "userInputTitle") + ?: return@runOnQspThread + val input = doShow.outTextValue + QSPSetInputStrText(input) if (!QSPExecUserInput(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onUseExecutorString() { - if (gameInterface == null) return; - runOnQspThread(() -> { - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_EXECUTOR, "execStringTitle"); - if (doShow == null) return; - var input = doShow.outTextValue; + override fun onUseExecutorString() { + runOnQspThread { + val doShow = + gameInterface.showLibDialog(LibTypeDialog.DIALOG_EXECUTOR, "execStringTitle") + ?: return@runOnQspThread + val input = doShow.outTextValue if (!QSPExecString(input, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void execute(final String code) { - runOnQspThread(() -> { + override fun execute(code: String?) { + runOnQspThread { if (!QSPExecString(code, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void executeCounter() { - if (libLock.isLocked()) return; - runOnQspThread(() -> { + override fun executeCounter() { + if (libLock.isLocked) return + runOnQspThread { if (!QSPExecCounter(true)) { - showLastQspError(); + showLastQspError() } - }); - } - - @Override - public LibGameState getGameState() { - return gameState; + } } - @Override - public void setGameInterface(GameInterface inter) { - this.gameInterface = inter; + override fun setGameInterface(inter: GameInterface) { + this.gameInterface = inter } // endregion LibQpProxy - // region LibQpCallbacks - - @Override - public void RefreshInt() { - var request = new LibRefIRequest(); - var configChanged = loadInterfaceConfiguration(); + override fun RefreshInt() { + val request = LibRefIRequest() + val configChanged = loadInterfaceConfiguration() if (configChanged) { - request.isIConfigChanged = true; + request.isIConfigChanged = true } if (QSPIsMainDescChanged()) { - if (gameState.mainDesc != null) { - if (!gameState.mainDesc.equals(QSPGetMainDesc())) { - gameState.mainDesc = QSPGetMainDesc(); - request.isMainDescChanged = true; - } - } else { - gameState.mainDesc = QSPGetMainDesc(); - request.isMainDescChanged = true; + if (gameState.mainDesc != QSPGetMainDesc()) { + gameState.mainDesc = QSPGetMainDesc() + request.isMainDescChanged = true } } if (QSPIsActionsChanged()) { - if (gameState.actionsList != null) { - if (gameState.actionsList != getActionsList()) { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; - } - } else { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; + if (gameState.actionsList !== actionsList) { + gameState.actionsList = actionsList + request.isActionsChanged = true } } if (QSPIsObjectsChanged()) { - if (gameState.objectsList != null) { - if (gameState.objectsList != getObjectsList()) { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; - } - } else { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; + if (gameState.objectsList !== objectsList) { + gameState.objectsList = objectsList + request.isObjectsChanged = true } } if (QSPIsVarsDescChanged()) { - if (gameState.varsDesc != null) { - if (!gameState.varsDesc.equals(QSPGetVarsDesc())) { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; - } - } else { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; + if (gameState.varsDesc != QSPGetVarsDesc()) { + gameState.varsDesc = QSPGetVarsDesc() ?: "" + request.isVarsDescChanged = true } } - var inter = gameInterface; - if (inter != null) { - inter.doRefresh(request); - } + val inter = gameInterface + inter.doRefresh(request) } - @Override - public void ShowPicture(String path) { - var inter = gameInterface; - if (inter == null) return; - if (!isNotEmptyOrBlank(path)) return; - inter.showLibDialog(LibTypeDialog.DIALOG_PICTURE, path); + override fun ShowPicture(path: String) { + val inter = gameInterface + if (!isNotEmptyOrBlank(path)) return + inter.showLibDialog(LibTypeDialog.DIALOG_PICTURE, path) } - @Override - public void SetTimer(int msecs) { - var inter = gameInterface; - if (inter == null) return; - inter.setCountInter(msecs); + override fun SetTimer(msecs: Int) { + val inter = gameInterface + inter.setCountInter(msecs) } - @Override - public void ShowMessage(String message) { - var inter = gameInterface; - if (inter == null) return; - inter.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, message); + override fun ShowMessage(message: String) { + val inter = gameInterface + inter.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, message) } - @Override - public void PlayFile(String path, int volume) { - if (gameInterface == null) return; - if (!isNotEmptyOrBlank(path)) return; - gameInterface.playFile(path, volume); + override fun PlayFile(path: String, volume: Int) { + if (!isNotEmptyOrBlank(path)) return + gameInterface.playFile(path, volume) } - @Override - public boolean IsPlayingFile(final String path) { - if (gameInterface == null) return false; - return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path); + override fun IsPlayingFile(path: String): Boolean { + return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path) } - @Override - public void CloseFile(String path) { - if (gameInterface == null) return; + override fun CloseFile(path: String) { if (isNotEmptyOrBlank(path)) { - gameInterface.closeFile(path); + gameInterface.closeFile(path) } else { - gameInterface.closeAllFiles(); + gameInterface.closeAllFiles() } } - @Override - public void OpenGame(String filename) { + override fun OpenGame(filename: String?) { if (filename == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) } else { - if (gameInterface == null) return; try { - var saveFile = fromFullPath(context, filename); + val saveFile = fromFullPath(context, filename) ?: return + gameInterface.requestPermFile(saveFile.uri) if (isWritableFile(context, saveFile)) { - if (saveFile == null) return; - gameInterface.doWithCounterDisabled(() -> loadGameState(saveFile.getUri())); + gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } } else { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found"); - Log.e(TAG, "Save file not found"); - } - } catch (Exception e) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found") } - Log.e(TAG, "Error: ", e); + } catch (e: Exception) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()) } } } - @Override - public void SaveGame(String filename) { + override fun SaveGame(filename: String?) { if (filename == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_SAVE, null); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_SAVE, null) } else { - var save = new File(filename); - var saveFile = findOrCreateFile(context, getCurGameDir(), save.getName(), MimeType.TEXT); - if (isWritableFile(context, saveFile)) { - if (saveFile == null) return; - saveGameState(saveFile.getUri()); + val currGameDir = currGameDir ?: return + val saveFileUri = gameInterface.requestCreateFile(currGameDir.uri, filename) + if (saveFileUri != Uri.EMPTY) { + saveGameState(saveFileUri) } else { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir"); - } - Log.e(TAG, "Error access dir"); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir") + Log.e(TAG, "Error access dir") } } } - @Override - public String InputBox(String prompt) { - if (gameInterface == null) return ""; - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, prompt); - if (doShow == null) return ""; - return doShow.outTextValue; + override fun InputBox(prompt: String): String { + val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, prompt) ?: return "" + return doShow.outTextValue } - @Override - public int GetMSCount() { - var now = SystemClock.elapsedRealtime(); - if (lastMsCountCallTime == 0) { - lastMsCountCallTime = gameStartTime; + override fun GetMSCount(): Int { + val now = SystemClock.elapsedRealtime() + if (lastMsCountCallTime == 0L) { + lastMsCountCallTime = gameStartTime } - var dt = (int) (now - lastMsCountCallTime); - lastMsCountCallTime = now; - return dt; + val dt = (now - lastMsCountCallTime).toInt() + lastMsCountCallTime = now + return dt } - @Override - public void AddMenuItem(String name, String imgPath) { - var item = new LibMenuItem(); - item.name = name; - item.pathToImage = imgPath; - gameState.menuItemsList.add(item); + override fun AddMenuItem(name: String, imgPath: String) { + val item = LibMenuItem() + item.name = name + item.pathToImage = imgPath + gameState.menuItemsList.add(item) } - @Override - public void ShowMenu() { - if (gameInterface == null) return; - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_MENU, null); - if (doShow == null) return; - var result = doShow.outNumValue; + override fun ShowMenu() { + val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_MENU, null) ?: return + val result = doShow.outNumValue if (result != -1) { - QSPSelectMenuItem(result); + QSPSelectMenuItem(result) } } - @Override - public void DeleteMenu() { - gameState.menuItemsList.clear(); + override fun DeleteMenu() { + gameState.menuItemsList.clear() } - @Override - public void Wait(int msecs) { + override fun Wait(msecs: Int) { try { - Thread.sleep(msecs); - } catch (InterruptedException ex) { - Log.e(TAG,"Wait failed", ex); + Thread.sleep(msecs.toLong()) + } catch (ex: InterruptedException) { + Log.e(TAG, "Wait failed", ex) } } - @Override - public void ShowWindow(int type, boolean isShow) { - if (gameInterface == null) return; - var windowType = LibTypeWindow.values()[type]; - gameInterface.changeVisWindow(windowType, isShow); + override fun ShowWindow(type: Int, isShow: Boolean) { + val windowType = LibTypeWindow.entries[type] + gameInterface.changeVisWindow(windowType, isShow) } - @Override - public byte[] GetFileContents(String path) { - var targetFile = fromFullPath(context, path); - if (targetFile == null) return null; - var targetFileUri = targetFile.getUri(); - return getFileContents(context , targetFileUri); + override fun GetFileContents(path: String): ByteArray? { + val targetFile = fromFullPath(context, path) ?: return null + val targetFileUri = targetFile.uri + gameInterface.requestPermFile(targetFileUri) + return getFileContents(context, targetFileUri)!! } - @Override - public void ChangeQuestPath(String path) { - var newGameDir = fromFullPath(context, path); + override fun ChangeQuestPath(path: String) { + val newGameDir = fromFullPath(context, path) if (newGameDir == null || !newGameDir.exists()) { - Log.e(TAG,"Game directory not found: " + path); - return; + Log.e(TAG, "Game directory not found: $path") + return } - var currGameDirUri = getCurGameDir().getUri(); - var newGameDirUri = newGameDir.getUri(); - if (!Objects.equals(currGameDirUri, newGameDirUri)) { - gameState.gameDirUri = newGameDirUri; - gameInterface.doChangeCurrGameDir(newGameDirUri); + val currGameDirUri = currGameDir?.uri + val newGameDirUri = newGameDir.uri + if (currGameDirUri != newGameDirUri) { + gameState.gameDirUri = newGameDirUri + gameInterface.doChangeCurrGameDir(newGameDirUri) } - } - - // endregion LibQpCallbacks + } // endregion LibQpCallbacks } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index af40ffa..3abc41b 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -1,605 +1,519 @@ -package org.qp.android.questopiabundle.lib.impl; - -import static org.qp.android.questopiabundle.utils.FileUtil.documentWrap; -import static org.qp.android.questopiabundle.utils.FileUtil.findOrCreateFile; -import static org.qp.android.questopiabundle.utils.FileUtil.fromFullPath; -import static org.qp.android.questopiabundle.utils.FileUtil.fromRelPath; -import static org.qp.android.questopiabundle.utils.FileUtil.getFileContents; -import static org.qp.android.questopiabundle.utils.FileUtil.isWritableFile; -import static org.qp.android.questopiabundle.utils.FileUtil.writeFileContents; -import static org.qp.android.questopiabundle.utils.HtmlUtil.getSrcDir; -import static org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags; -import static org.qp.android.questopiabundle.utils.HtmlUtil.removeHtmlTags; -import static org.qp.android.questopiabundle.utils.PathUtil.getFilename; -import static org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath; -import static org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty; -import static org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank; -import static org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread; - -import android.content.Context; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.documentfile.provider.DocumentFile; - -import com.anggrayudi.storage.file.DocumentFileCompat; -import com.anggrayudi.storage.file.MimeType; - -import org.libsnxqsp.jni.SNXLib; -import org.qp.android.questopiabundle.GameInterface; -import org.qp.android.questopiabundle.dto.LibListItem; -import org.qp.android.questopiabundle.dto.LibMenuItem; -import org.qp.android.questopiabundle.lib.LibGameState; -import org.qp.android.questopiabundle.lib.LibIProxy; -import org.qp.android.questopiabundle.lib.LibRefIRequest; -import org.qp.android.questopiabundle.lib.LibTypeDialog; -import org.qp.android.questopiabundle.lib.LibTypeWindow; - -import java.io.File; -import java.util.ArrayList; -import java.util.Locale; -import java.util.concurrent.locks.ReentrantLock; - -public class LibCharlieProxyImpl extends SNXLib implements LibIProxy { - private final String TAG = "LibProxyImpl"; - - private final ReentrantLock libLock = new ReentrantLock(); - private final LibGameState gameState = new LibGameState(); - private final Context context; - private Thread libThread; - private volatile Handler libHandler; - private volatile boolean libThreadInit; - private volatile long gameStartTime; - private volatile long lastMsCountCallTime; - private GameInterface gameInterface; - - public LibCharlieProxyImpl(Context context) { - this.context = context; - } - - private DocumentFile getCurGameDir() { - return DocumentFileCompat.fromUri(context, gameState.gameDirUri); - } - - private synchronized void runOnQspThread(final Runnable runnable) { - if (libThread == null) { - Log.w(TAG, "Lib thread has not been started!"); - return; - } +package org.qp.android.questopiabundle.lib.impl + +import android.content.Context +import android.net.Uri +import android.os.Handler +import android.os.Looper +import android.os.SystemClock +import android.util.Log +import androidx.documentfile.provider.DocumentFile +import com.anggrayudi.storage.file.DocumentFileCompat.fromUri +import org.libsnxqsp.jni.SNXLib +import org.qp.android.questopiabundle.GameInterface +import org.qp.android.questopiabundle.dto.LibListItem +import org.qp.android.questopiabundle.dto.LibMenuItem +import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.lib.LibIProxy +import org.qp.android.questopiabundle.lib.LibRefIRequest +import org.qp.android.questopiabundle.lib.LibTypeDialog +import org.qp.android.questopiabundle.lib.LibTypeWindow +import org.qp.android.questopiabundle.utils.FileUtil.documentWrap +import org.qp.android.questopiabundle.utils.FileUtil.fromFullPath +import org.qp.android.questopiabundle.utils.FileUtil.fromRelPath +import org.qp.android.questopiabundle.utils.FileUtil.getFileContents +import org.qp.android.questopiabundle.utils.FileUtil.isWritableFile +import org.qp.android.questopiabundle.utils.FileUtil.writeFileContents +import org.qp.android.questopiabundle.utils.HtmlUtil.getSrcDir +import org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags +import org.qp.android.questopiabundle.utils.HtmlUtil.removeHtmlTags +import org.qp.android.questopiabundle.utils.PathUtil.getFilename +import org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath +import org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty +import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank +import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread +import java.util.* +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.Volatile + +class LibCharlieProxyImpl( + private val context: Context, + override val gameState: LibGameState = LibGameState() +) : SNXLib(), LibIProxy { + + private val TAG = javaClass.simpleName + private val libLock = ReentrantLock() + private lateinit var libThread: Thread + @Volatile private lateinit var libHandler: Handler + @Volatile private var libThreadInit = false + @Volatile private var gameStartTime: Long = 0L + @Volatile private var lastMsCountCallTime: Long = 0L + private lateinit var gameInterface: GameInterface + private val currGameDir: DocumentFile? + get() = fromUri(context, gameState.gameDirUri) + + @Synchronized + private fun runOnQspThread(runnable: Runnable) { if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!"); - return; + Log.w(TAG, "Lib thread has been started, but not initialized!") + return } - var mLibHandler = libHandler; - if (mLibHandler == null) return; - mLibHandler.post(() -> { - libLock.lock(); + val mLibHandler = libHandler + mLibHandler.post { + libLock.lock() try { - runnable.run(); + runnable.run() } finally { - libLock.unlock(); + libLock.unlock() } - }); + } } - private boolean loadGameWorld() { - var gameFileUri = gameState.gameFileUri; - var gameFile = DocumentFileCompat.fromUri(context, gameState.gameFileUri); - var gameFileFullPath = documentWrap(gameFile).getAbsolutePath(context); - var gameData = getFileContents(context, gameFileUri); - if (gameData == null) return false; + private fun loadGameWorld(): Boolean { + val gameFileUri = gameState.gameFileUri + val gameFile = fromUri(context, gameState.gameFileUri) + val gameFileFullPath = documentWrap(gameFile!!).getAbsolutePath(context) + val gameData = getFileContents(context, gameFileUri) ?: return false - if (!loadGameWorldFromData(gameData, gameData.length, gameFileFullPath)) { - showLastQspError(); - Log.d("QSP", "World is not loaded!"); - return false; + if (!loadGameWorldFromData(gameData, gameData.size, gameFileFullPath)) { + showLastQspError() + return false } - Log.d("QSP", "World is loaded!"); - return true; + + return true } - private void showLastQspError() { - var errorData = getLastErrorData(); - var locName = getStringOrEmpty(errorData.locName()); - var desc = getStringOrEmpty(getErrorDesc(errorData.errorNum())); - final var message = String.format( - Locale.getDefault(), - "Location: %s\nAction: %d\nLine: %d\nError number: %d\nDescription: %s", - locName, - errorData.index(), - errorData.line(), - errorData.errorNum(), - desc); - Log.e(TAG, errorData.toString()); - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, message); - } + private fun showLastQspError() { + val errorData = lastErrorData + val locName = getStringOrEmpty(errorData.locName) + val desc = getStringOrEmpty(getErrorDesc(errorData.errorNum)) + val message = String.format( + Locale.getDefault(), + "Location: %s\nAction: %d\nLine: %d\nError number: %d\nDescription: %s", + locName, + errorData.index, + errorData.line, + errorData.errorNum, + desc + ) + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, message) } /** * Loads the interface configuration - using HTML, font and colors - from the library. * - * @return true if the configuration has changed, otherwise false + * @return `true` if the configuration has changed, otherwise `false` */ - private boolean loadInterfaceConfiguration() { - var config = gameState.interfaceConfig; - boolean changed = false; + private fun loadInterfaceConfiguration(): Boolean { + val config = gameState.interfaceConfig + var changed = false - var htmlResult = (SNXLib.VarValResp) QSPGetVarValues("USEHTML", 0); - if (htmlResult.isSuccess()) { - boolean useHtml = htmlResult.intValue() != 0; + val htmlResult = QSPGetVarValues("USEHTML", 0) as VarValResp + if (htmlResult.isSuccess) { + val useHtml = htmlResult.intValue != 0 if (config.useHtml != useHtml) { - config.useHtml = useHtml; - changed = true; + config.useHtml = useHtml + changed = true } } - var fSizeResult = (SNXLib.VarValResp) QSPGetVarValues("FSIZE", 0); - if (fSizeResult.isSuccess() && config.fontSize != fSizeResult.intValue()) { - config.fontSize = fSizeResult.intValue(); - changed = true; + val fSizeResult = QSPGetVarValues("FSIZE", 0) as VarValResp + if (fSizeResult.isSuccess && config.fontSize != fSizeResult.intValue.toLong()) { + config.fontSize = fSizeResult.intValue.toLong() + changed = true } - var bColorResult = (SNXLib.VarValResp) QSPGetVarValues("BCOLOR", 0); - if (bColorResult.isSuccess() && config.backColor != bColorResult.intValue()) { - config.backColor = bColorResult.intValue(); - changed = true; + val bColorResult = QSPGetVarValues("BCOLOR", 0) as VarValResp + if (bColorResult.isSuccess && config.backColor != bColorResult.intValue.toLong()) { + config.backColor = bColorResult.intValue.toLong() + changed = true } - var fColorResult = (SNXLib.VarValResp) QSPGetVarValues("FCOLOR", 0); - if (fColorResult.isSuccess() && config.fontColor != fColorResult.intValue()) { - config.fontColor = fColorResult.intValue(); - changed = true; + val fColorResult = QSPGetVarValues("FCOLOR", 0) as VarValResp + if (fColorResult.isSuccess && config.fontColor != fColorResult.intValue.toLong()) { + config.fontColor = fColorResult.intValue.toLong() + changed = true } - var lColorResult = (SNXLib.VarValResp) QSPGetVarValues("LCOLOR", 0); - if (lColorResult.isSuccess() && config.linkColor != lColorResult.intValue()) { - config.linkColor = lColorResult.intValue(); - changed = true; + val lColorResult = QSPGetVarValues("LCOLOR", 0) as VarValResp + if (lColorResult.isSuccess && config.linkColor != lColorResult.intValue.toLong()) { + config.linkColor = lColorResult.intValue.toLong() + changed = true } - return changed; - } - - @NonNull - private ArrayList getActionsList() { - var actions = new ArrayList(); - var curGameDir = getCurGameDir(); - - for (var element : getActions()) { - var newElement = new LibListItem(element); - if (isNotEmptyOrBlank(newElement.pathToImage)) { - var tempPath = normalizeContentPath(getFilename(newElement.pathToImage)); - var fileFromPath = fromRelPath(context, tempPath, curGameDir, false); - if (fileFromPath != null) { - newElement.pathToImage = String.valueOf(fileFromPath.getUri()); + return changed + } + + private val actionsList: ArrayList + get() { + val actions = ArrayList() + val currGameDir = currGameDir + + for (element in getActions()) { + val newElement = LibListItem(element) + if (isNotEmptyOrBlank(newElement.pathToImage) && currGameDir != null) { + val tempPath = + normalizeContentPath(getFilename(newElement.pathToImage)) + val fileFromPath = + fromRelPath(context, tempPath, currGameDir, false) + if (fileFromPath != null) { + newElement.pathToImage = fileFromPath.uri.toString() + } else { + newElement.pathToImage = "" + } + } + newElement.text = if (gameState.interfaceConfig.useHtml) { + removeHtmlTags(newElement.text) } else { - newElement.pathToImage = null; + newElement.text } + actions.add(newElement) } - newElement.text = gameState.interfaceConfig.useHtml - ? removeHtmlTags(newElement.text) - : newElement.text; - actions.add(newElement); - } - return actions; - } - - @NonNull - private ArrayList getObjectsList() { - var objects = new ArrayList(); - var curGameDir = getCurGameDir(); + return actions + } - for (var element : getObjects()) { - var object = new LibListItem(element); - if (object.text.contains(" + get() { + val objects = ArrayList() + val currGameDir = currGameDir + + for (element in getObjects()) { + val newElement = LibListItem(element) + if (newElement.text.contains(" { - while (!Thread.currentThread().isInterrupted()) { + override fun startLibThread() { + libThread = Thread({ + while (!Thread.currentThread().isInterrupted) { try { - init(); + init() if (Looper.myLooper() == null) { - Looper.prepare(); + Looper.prepare() } - libHandler = new Handler(Looper.myLooper()); - libThreadInit = true; - Looper.loop(); - terminate(); - } catch (Throwable t) { - Log.e(TAG, "lib thread has stopped exceptionally", t); - Thread.currentThread().interrupt(); + libHandler = Handler(Looper.myLooper()!!) + libThreadInit = true + Looper.loop() + terminate() + } catch (t: Throwable) { + Log.e(TAG, "lib thread has stopped exceptionally", t) + Thread.currentThread().interrupt() } } - }, "libSNXQSP"); - libThread.start(); + }, "libSNXQSP") + libThread.start() } - public synchronized void stopLibThread() { - if (libThread == null) return; + @Synchronized + override fun stopLibThread() { if (libThreadInit) { - var handler = libHandler; - if (handler != null) { - handler.getLooper().quitSafely(); - } - libThreadInit = false; + val handler = libHandler + handler.looper.quitSafely() + libThreadInit = false } else { - Log.w(TAG, "lib thread has been started, but not initialized"); + Log.w(TAG, "lib thread has been started, but not initialized") } - libThread.interrupt(); + libThread.interrupt() } - public void enableDebugMode(boolean isDebug) { - runOnQspThread(() -> enableDebugMode(isDebug)); + override fun enableDebugMode(isDebug: Boolean) { + runOnQspThread { enableDebugMode(isDebug) } } - @Override - public void runGame(long gameId, - String gameTitle, - Uri gameDirUri, - Uri gameFileUri) { - runOnQspThread(() -> doRunGame(gameId, gameTitle, gameDirUri, gameFileUri)); + override fun runGame( + gameId: Long, + gameTitle: String, + gameDirUri: Uri, + gameFileUri: Uri + ) { + runOnQspThread { doRunGame(gameId, gameTitle, gameDirUri, gameFileUri) } } - @Override - public void restartGame() { - runOnQspThread(() -> doRunGame(gameState.gameId, gameState.gameTitle, gameState.gameDirUri, gameState.gameFileUri)); + override fun restartGame() { + runOnQspThread { + doRunGame( + gameState.gameId, + gameState.gameTitle, + gameState.gameDirUri, + gameState.gameFileUri + ) + } } - private void doRunGame(final long id, final String title, final Uri dir, final Uri file) { - gameInterface.doWithCounterDisabled(() -> { - gameInterface.closeAllFiles(); - gameState.reset(); - gameState.gameRunning = true; - gameState.gameId = id; - gameState.gameTitle = title; - gameState.gameDirUri = dir; - gameState.gameFileUri = file; - gameInterface.doChangeCurrGameDir(dir); - if (!loadGameWorld()) return; - gameStartTime = SystemClock.elapsedRealtime(); - lastMsCountCallTime = 0; + private fun doRunGame(id: Long, title: String, dir: Uri, file: Uri) { + gameInterface.doWithCounterDisabled { + gameInterface.closeAllFiles() + gameState.reset() + gameState.gameRunning = true + gameState.gameId = id + gameState.gameTitle = title + gameState.gameDirUri = dir + gameState.gameFileUri = file + gameInterface.doChangeCurrGameDir(dir) + if (!loadGameWorld()) return@doWithCounterDisabled + gameStartTime = SystemClock.elapsedRealtime() + lastMsCountCallTime = 0 if (!QSPRestartGame(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void loadGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> loadGameState(uri)); - return; + override fun loadGameState(uri: Uri) { + if (!isSameThread(libHandler.looper.thread)) { + runOnQspThread { loadGameState(uri) } + return } - final var gameData = getFileContents(context, uri); - if (gameData == null) return; - if (!QSPOpenSavedGameFromData(gameData, gameData.length, true)) { - showLastQspError(); + + gameInterface.requestPermFile(uri) + val gameData = getFileContents(context, uri) ?: return + if (!QSPOpenSavedGameFromData(gameData, gameData.size, true)) { + showLastQspError() } } - @Override - public void saveGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> saveGameState(uri)); - return; + override fun saveGameState(uri: Uri) { + if (!isSameThread(libHandler.looper.thread)) { + runOnQspThread { saveGameState(uri) } + return } - final var gameData = QSPSaveGameAsData(false); - if (gameData == null) return; - writeFileContents(context, uri, gameData); + + gameInterface.requestPermFile(uri) + val gameData = QSPSaveGameAsData(false) ?: return + writeFileContents(context, uri, gameData) } - @Override - public void onActionClicked(final int index) { - runOnQspThread(() -> { + override fun onActionClicked(index: Int) { + runOnQspThread { if (!setSelActionIndex(index, false)) { - showLastQspError(); + showLastQspError() } if (!executeSelActionCode(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onObjectSelected(final int index) { - runOnQspThread(() -> { + override fun onObjectSelected(index: Int) { + runOnQspThread { if (!setSelObjectIndex(index, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onInputAreaClicked() { - if (gameInterface == null) return; - runOnQspThread(() -> { - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, "userInputTitle"); - if (doShow == null) return; - var input = doShow.outTextValue; - QSPSetInputStrText(input); + override fun onInputAreaClicked() { + runOnQspThread { + val doShow = + gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, "userInputTitle") + ?: return@runOnQspThread + val input = doShow.outTextValue + QSPSetInputStrText(input) if (!execUserInput(true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void onUseExecutorString() { - if (gameInterface == null) return; - runOnQspThread(() -> { - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_EXECUTOR, "execStringTitle"); - if (doShow == null) return; - var input = doShow.outTextValue; + override fun onUseExecutorString() { + runOnQspThread { + val doShow = + gameInterface.showLibDialog(LibTypeDialog.DIALOG_EXECUTOR, "execStringTitle") + ?: return@runOnQspThread + val input = doShow.outTextValue if (!execString(input, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void execute(final String code) { - runOnQspThread(() -> { + override fun execute(code: String?) { + runOnQspThread { if (!execString(code, true)) { - showLastQspError(); + showLastQspError() } - }); + } } - @Override - public void executeCounter() { - if (libLock.isLocked()) return; - runOnQspThread(() -> { + override fun executeCounter() { + if (libLock.isLocked) return + runOnQspThread { if (!execCounter(true)) { - showLastQspError(); + showLastQspError() } - }); - } - - @Override - public LibGameState getGameState() { - return gameState; + } } - @Override - public void setGameInterface(GameInterface inter) { - this.gameInterface = inter; + override fun setGameInterface(inter: GameInterface) { + this.gameInterface = inter } - // endregion LibQpProxy - - // region LibQpCallbacks - - @Override - public void RefreshInt() { - var request = new LibRefIRequest(); - var configChanged = loadInterfaceConfiguration(); + override fun RefreshInt() { + val request = LibRefIRequest() + val configChanged = loadInterfaceConfiguration() if (configChanged) { - request.isIConfigChanged = true; + request.isIConfigChanged = true } if (QSPIsMainDescChanged()) { - if (gameState.mainDesc != null) { - if (!gameState.mainDesc.equals(getMainDesc())) { - gameState.mainDesc = getMainDesc(); - request.isMainDescChanged = true; - } - } else { - gameState.mainDesc = getMainDesc(); - request.isMainDescChanged = true; + if (gameState.mainDesc != mainDesc) { + gameState.mainDesc = mainDesc + request.isMainDescChanged = true } } - if (isActionsChanged()) { - if (gameState.actionsList != null) { - if (gameState.actionsList != getActionsList()) { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; - } - } else { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; + if (isActionsChanged) { + if (gameState.actionsList !== actionsList) { + gameState.actionsList = actionsList + request.isActionsChanged = true } } - if (isObjectsChanged()) { - if (gameState.objectsList != null) { - if (gameState.objectsList != getObjectsList()) { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; - } - } else { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; + if (isObjectsChanged) { + if (gameState.objectsList !== objectsList) { + gameState.objectsList = objectsList + request.isObjectsChanged = true } } - if (QSPIsVarsDescChanged()) { - if (gameState.varsDesc != null) { - if (!gameState.varsDesc.equals(QSPGetVarsDesc())) { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; - } - } else { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; + if (isVarsDescChanged) { + if (gameState.varsDesc !== QSPGetVarsDesc()) { + gameState.varsDesc = QSPGetVarsDesc() ?: "" + request.isVarsDescChanged = true } } - var inter = gameInterface; - if (inter != null) { - inter.doRefresh(request); - } + val inter = gameInterface + inter.doRefresh(request) } - @Override - public void ShowPicture(String path) { - var inter = gameInterface; - if (inter == null) return; - if (!isNotEmptyOrBlank(path)) return; - inter.showLibDialog(LibTypeDialog.DIALOG_PICTURE, path); + override fun ShowPicture(path: String) { + val inter = gameInterface + if (!isNotEmptyOrBlank(path)) return + inter.showLibDialog(LibTypeDialog.DIALOG_PICTURE, path) } - @Override - public void SetTimer(int msecs) { - var inter = gameInterface; - if (inter == null) return; - inter.setCountInter(msecs); + override fun SetTimer(msecs: Int) { + val inter = gameInterface + inter.setCountInter(msecs) } - @Override - public void ShowMessage(String message) { - var inter = gameInterface; - if (inter == null) return; - inter.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, message); + override fun ShowMessage(message: String) { + val inter = gameInterface + inter.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, message) } - @Override - public void PlayFile(String path, int volume) { - if (gameInterface == null) return; - if (!isNotEmptyOrBlank(path)) return; - gameInterface.playFile(path, volume); + override fun PlayFile(path: String, volume: Int) { + if (!isNotEmptyOrBlank(path)) return + gameInterface.playFile(path, volume) } - @Override - public boolean IsPlayingFile(final String path) { - if (gameInterface == null) return false; - return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path); + override fun IsPlayingFile(path: String): Boolean { + return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path) } - @Override - public void CloseFile(String path) { - if (gameInterface == null) return; + override fun CloseFile(path: String) { if (isNotEmptyOrBlank(path)) { - gameInterface.closeFile(path); + gameInterface.closeFile(path) } else { - gameInterface.closeAllFiles(); + gameInterface.closeAllFiles() } } - @Override - public void OpenGame(String filename) { + override fun OpenGame(filename: String?) { if (filename == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) } else { try { - var saveFile = fromFullPath(context, filename); - if (!isWritableFile(context, saveFile)) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found"); - } - Log.e(TAG, "Save file not found"); - return; - } - if (gameInterface != null) { - gameInterface.doWithCounterDisabled(() -> loadGameState(saveFile.getUri())); - } - } catch (Exception e) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()); + val saveFile = fromFullPath(context, filename) ?: return + gameInterface.requestPermFile(saveFile.uri) + if (isWritableFile(context, saveFile)) { + gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } + } else { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found") + Log.e(TAG, "Save file not found") } - Log.e(TAG, "Error: ", e); + } catch (e: Exception) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()) + Log.e(TAG, "Error: ", e) } } } - @Override - public void SaveGame(String filename) { + override fun SaveGame(filename: String?) { if (filename == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_SAVE, null); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_SAVE, null) } else { - var save = new File(filename); - var saveFile = findOrCreateFile(context, getCurGameDir(), save.getName(), MimeType.TEXT); - if (isWritableFile(context, saveFile)) { - saveGameState(saveFile.getUri()); + val currGameDir = currGameDir ?: return + val saveFileUri = gameInterface.requestCreateFile(currGameDir.uri, filename) + if (saveFileUri != Uri.EMPTY) { + saveGameState(saveFileUri) } else { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir"); - } - Log.e(TAG, "Error access dir"); + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir") + Log.e(TAG, "Error access dir") } } } - @Override - public String InputBox(String prompt) { - if (gameInterface == null) return ""; - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, prompt); - if (doShow == null) return ""; - return doShow.outTextValue; + override fun InputBox(prompt: String): String { + val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, prompt) ?: return "" + return doShow.outTextValue } - @Override - public int GetMSCount() { - var now = SystemClock.elapsedRealtime(); - if (lastMsCountCallTime == 0) { - lastMsCountCallTime = gameStartTime; + override fun GetMSCount(): Int { + val now = SystemClock.elapsedRealtime() + if (lastMsCountCallTime == 0L) { + lastMsCountCallTime = gameStartTime } - var dt = (int) (now - lastMsCountCallTime); - lastMsCountCallTime = now; - return dt; + val dt = (now - lastMsCountCallTime).toInt() + lastMsCountCallTime = now + return dt } - @Override - public void addMenuItem(String name, String imgPath) { - var item = new LibMenuItem(); - item.name = name; - item.pathToImage = imgPath; - gameState.menuItemsList.add(item); + override fun addMenuItem(name: String, imgPath: String) { + val item = LibMenuItem() + item.name = name + item.pathToImage = imgPath + gameState.menuItemsList.add(item) } - @Override - public int showMenu() { - if (gameInterface == null) return super.showMenu(); - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_MENU, null); - if (doShow == null) return super.showMenu(); - var result = doShow.outNumValue; + override fun showMenu(): Int { + val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_MENU, null) + ?: return super.showMenu() + val result = doShow.outNumValue if (result != -1) { - return result; + return result } - return super.showMenu(); + return super.showMenu() } - @Override - public void deleteMenu() { - gameState.menuItemsList.clear(); + override fun deleteMenu() { + gameState.menuItemsList.clear() } - @Override - public void Wait(int msecs) { + override fun Wait(msecs: Int) { try { - Thread.sleep(msecs); - } catch (InterruptedException ex) { - Log.e(TAG,"Wait failed", ex); + Thread.sleep(msecs.toLong()) + } catch (ex: InterruptedException) { + Log.e(TAG, "Wait failed", ex) } } - @Override - public void ShowWindow(int type, boolean isShow) { - if (gameInterface == null) return; - var windowType = LibTypeWindow.values()[type]; - gameInterface.changeVisWindow(windowType, isShow); + override fun ShowWindow(type: Int, isShow: Boolean) { + val windowType = LibTypeWindow.entries[type] + gameInterface.changeVisWindow(windowType, isShow) } - - // endregion LibQpCallbacks } diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt index 86502e6..f3f58d4 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt @@ -3,28 +3,22 @@ package org.qp.android.questopiabundle.utils import android.content.Context import android.net.Uri import android.util.Log -import androidx.annotation.NonNull import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.FileWrapper -import com.anggrayudi.storage.file.CreateMode import com.anggrayudi.storage.file.DocumentFileCompat import com.anggrayudi.storage.file.child import com.anggrayudi.storage.file.isWritable -import com.anggrayudi.storage.file.makeFile import org.qp.android.questopiabundle.utils.StreamUtil.copy import java.io.ByteArrayOutputStream import java.io.IOException object FileUtil { - - @JvmStatic fun isWritableFile(context: Context, file: DocumentFile?): Boolean { if (file == null) return false val canWrite = file.isWritable(context) return file.exists() && file.isFile && canWrite } - @JvmStatic fun getFileContents( context: Context, uriContent: Uri @@ -48,12 +42,10 @@ object FileUtil { } } - @JvmStatic fun documentWrap(inputFile: DocumentFile): FileWrapper.Document { return FileWrapper.Document(inputFile) } - @JvmStatic fun writeFileContents( context: Context, uriContent: Uri, @@ -74,17 +66,6 @@ object FileUtil { } } - @JvmStatic - fun findOrCreateFile( - context: Context, - srcDir: DocumentFile, - name: String, - mimeType: String? - ): DocumentFile? { - return srcDir.makeFile(context, name, mimeType, CreateMode.REUSE) - } - - @JvmStatic fun fromRelPath( context: Context, path: String, @@ -94,7 +75,6 @@ object FileUtil { return parentDir.child(context, path, requiresWriteAccess) } - @JvmStatic fun fromFullPath( context: Context, fullPath: String diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt index 3adb22f..02e1de2 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt @@ -6,7 +6,6 @@ import org.qp.android.questopiabundle.utils.StringUtil.isNullOrEmpty import java.util.regex.Pattern object HtmlUtil { - private val HTML_PATTERN: Pattern = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>") @JvmStatic diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt index e8675ed..9d48bbe 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt @@ -1,8 +1,6 @@ package org.qp.android.questopiabundle.utils object PathUtil { - - @JvmStatic fun getFilename(path: String): String { val idx = path.lastIndexOf('/') return if (idx == -1) path else path.substring(idx + 1) @@ -13,7 +11,6 @@ object PathUtil { * * @implNote Removes "./" from the beginning of the path, replaces all occurrences of "\" with "/". */ - @JvmStatic fun normalizeContentPath(path: String?): String { if (path == null) return "" var result: String = path diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt index 0fcc090..e9142b7 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt @@ -5,13 +5,11 @@ import java.io.InputStream import java.io.OutputStream object StreamUtil { - private const val BUFFER_SIZE = 8192 /** * Copies data from the `from` stream to the `to` stream. */ - @JvmStatic @Throws(IOException::class) fun copy(from: InputStream, to: OutputStream) { val buffer = ByteArray(BUFFER_SIZE) diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt index 902f40b..b5f9a73 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt @@ -3,18 +3,14 @@ package org.qp.android.questopiabundle.utils import org.jetbrains.annotations.Contract object StringUtil { - - @JvmStatic fun isNotEmptyOrBlank(str: String?): Boolean { return !str.isNullOrEmpty() && str.isNotBlank() } - @JvmStatic fun isNullOrEmpty(str: String?): Boolean { return str.isNullOrEmpty() } - @JvmStatic @Contract(value = "!null -> param1", pure = true) fun getStringOrEmpty(str: String?): String { return str ?: "" diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt index ce3068f..744f863 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt @@ -1,11 +1,9 @@ package org.qp.android.questopiabundle.utils object ThreadUtil { - /** * @return `true` if the current thread is `thread`, otherwise `false` */ - @JvmStatic fun isSameThread(thread: Thread): Boolean { return Thread.currentThread() == thread } From 7ababf47fcc9128ea347b134d435a7111cc77d58 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:32:41 +0300 Subject: [PATCH 17/57] Add a check for the image field to be null --- .../java/org/qp/android/questopiabundle/dto/LibListItem.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt index 333e7ef..8454efd 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt @@ -11,11 +11,11 @@ data class LibListItem( var pathToImage: String = "" ) : Parcelable { - constructor(item: QSPLib.ListItem) : this(item.name, item.image) + constructor(item: QSPLib.ListItem) : this(item.name, item.image ?: "") - constructor(item: NDKLib.ListItem) : this(item.text, item.image) + constructor(item: NDKLib.ListItem) : this(item.text, item.image ?: "") - constructor(item: SNXLib.ListItem) : this(item.text, item.image) + constructor(item: SNXLib.ListItem) : this(item.text, item.image ?: "") constructor(source: Parcel) : this( source.readString() ?: "", From 30a1abc9a502ad7f3a0298871154d0ab8843fdd8 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:33:50 +0300 Subject: [PATCH 18/57] Remove unnecessary nesting --- .../java/org/qp/android/questopiabundle/lib/LibGameState.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt index 91e5f72..a636f37 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt @@ -23,7 +23,7 @@ data class LibGameState( constructor(source: Parcel) : this ( source.readParcelable(LibIConfig::class.java.classLoader) ?: LibIConfig(), - source.readByte().toInt() != 0, + source.readInt() != 0, source.readLong(), source.readString() ?: "", source.readParcelable(Uri::class.java.classLoader) ?: Uri.EMPTY, @@ -38,7 +38,7 @@ data class LibGameState( override fun writeToParcel(dest: Parcel, flags: Int) { dest.writeParcelable(interfaceConfig, flags) - dest.writeByte((if (gameRunning) 1 else 0).toByte()) + dest.writeInt(if (gameRunning) 1 else 0) dest.writeLong(gameId) dest.writeString(gameTitle) dest.writeParcelable(gameDirUri, flags) From 73c28672982ce166f32525b100d596eb69736618 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:38:18 +0300 Subject: [PATCH 19/57] Add a check for equality of references --- .../qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt | 2 +- .../qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt | 2 +- .../qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 3c65b4d..7686cae 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -367,7 +367,7 @@ class LibAlphaProxyImpl( } if (isMainDescChanged) { if (isNotEmptyOrBlank(gameState.mainDesc)) { - if (gameState.mainDesc != mainDesc) { + if (gameState.mainDesc !== mainDesc) { gameState.mainDesc = mainDesc request.isMainDescChanged = true } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 94a8521..e5d15ab 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -379,7 +379,7 @@ class LibBravoProxyImpl( request.isIConfigChanged = true } if (QSPIsMainDescChanged()) { - if (gameState.mainDesc != QSPGetMainDesc()) { + if (gameState.mainDesc !== QSPGetMainDesc()) { gameState.mainDesc = QSPGetMainDesc() request.isMainDescChanged = true } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index 3abc41b..01a9121 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -372,7 +372,7 @@ class LibCharlieProxyImpl( request.isIConfigChanged = true } if (QSPIsMainDescChanged()) { - if (gameState.mainDesc != mainDesc) { + if (gameState.mainDesc !== mainDesc) { gameState.mainDesc = mainDesc request.isMainDescChanged = true } From c3e6af9c0064d3efeb3fb8e95652619d808606b7 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:40:13 +0300 Subject: [PATCH 20/57] Fix the error of using the wrong array --- libbravo/src/main/jni/src/bindings/android/android_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbravo/src/main/jni/src/bindings/android/android_control.c b/libbravo/src/main/jni/src/bindings/android/android_control.c index b7670e9..b2f4aae 100644 --- a/libbravo/src/main/jni/src/bindings/android/android_control.c +++ b/libbravo/src/main/jni/src/bindings/android/android_control.c @@ -254,7 +254,7 @@ JNIEXPORT jobjectArray JNICALL Java_org_libndkqsp_jni_NDKLib_QSPGetObjectData(JN jobjectArray res = (*env)->NewObjectArray(env, qspCurActionsCount, ndkListItemClass, 0); for (i = 0; i < qspCurActionsCount; ++i) { - item = ndkToJavaListItem(env, qspCurActions[i].Image, qspCurActions[i].Desc); + item = ndkToJavaListItem(env, qspCurObjects[i].Image, qspCurObjects[i].Desc); (*env)->SetObjectArrayElement(env, res, i, item.ListItem); } return res; From 623b36ebda25526f8a7c7e248d0a1c5d59d837c1 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 7 Mar 2025 06:54:22 +0300 Subject: [PATCH 21/57] Add a new function to check a string for nullability --- .../java/org/qp/android/questopiabundle/utils/StringUtil.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt index b5f9a73..19d3360 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt @@ -3,6 +3,10 @@ package org.qp.android.questopiabundle.utils import org.jetbrains.annotations.Contract object StringUtil { + fun isEmptyOrBlank(str: String?): Boolean { + return str.isNullOrEmpty() || str.isBlank() + } + fun isNotEmptyOrBlank(str: String?): Boolean { return !str.isNullOrEmpty() && str.isNotBlank() } From a75f3839ff0f24e77cffda69142c693aed612b17 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 7 Mar 2025 06:57:14 +0300 Subject: [PATCH 22/57] Refactor `HtmlUtil` * Deleted an unused annotation --- .../main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt index 02e1de2..47429aa 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt @@ -8,12 +8,10 @@ import java.util.regex.Pattern object HtmlUtil { private val HTML_PATTERN: Pattern = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>") - @JvmStatic fun isContainsHtmlTags(text: String): Boolean { return HTML_PATTERN.matcher(text).find() } - @JvmStatic fun getSrcDir(html: String): String { val document = Jsoup.parse(html) val imageElement = document.select("img").first() ?: return "" @@ -23,7 +21,6 @@ object HtmlUtil { /** * Remove HTML tags from the `html` string and return the resulting string. */ - @JvmStatic fun removeHtmlTags(html: String): String { if (isNullOrEmpty(html)) return "" From 585e4a5453b882224fb08b267a790dc7e8edf444 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 7 Mar 2025 06:57:57 +0300 Subject: [PATCH 23/57] Refactor `FileUtil` * Added a new method for checking directories --- .../java/org/qp/android/questopiabundle/utils/FileUtil.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt index f3f58d4..d712d1c 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt @@ -15,8 +15,12 @@ import java.io.IOException object FileUtil { fun isWritableFile(context: Context, file: DocumentFile?): Boolean { if (file == null) return false - val canWrite = file.isWritable(context) - return file.exists() && file.isFile && canWrite + return file.exists() && file.isFile && file.isWritable(context) + } + + fun isWritableDir(context: Context, dir: DocumentFile?): Boolean { + if (dir == null) return false + return dir.exists() && dir.isDirectory && dir.isWritable(context) } fun getFileContents( From 3c84ee66dc643363dc385eef6d5ecb934b86587d Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 7 Mar 2025 06:59:19 +0300 Subject: [PATCH 24/57] Make data classes immutable --- .../questopiabundle/lib/LibGameState.kt | 38 ++++++------------- .../android/questopiabundle/lib/LibIConfig.kt | 18 +++------ .../questopiabundle/lib/LibRefIRequest.kt | 10 ++--- 3 files changed, 22 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt index a636f37..31ec137 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt @@ -8,20 +8,20 @@ import org.qp.android.questopiabundle.dto.LibMenuItem import org.qp.android.questopiabundle.lib.LibIConfig data class LibGameState( - var interfaceConfig: LibIConfig = LibIConfig(), - var gameRunning: Boolean = false, - var gameId: Long = 0L, - var gameTitle: String = "", - var gameDirUri: Uri = Uri.EMPTY, - var gameFileUri: Uri = Uri.EMPTY, - var mainDesc: String = "", - var varsDesc: String = "", - var actionsList: MutableList = mutableListOf(), - var objectsList: MutableList = mutableListOf(), - var menuItemsList: MutableList = mutableListOf() + val interfaceConfig: LibIConfig = LibIConfig(), + val gameRunning: Boolean = false, + val gameId: Long = 0L, + val gameTitle: String = "", + val gameDirUri: Uri = Uri.EMPTY, + val gameFileUri: Uri = Uri.EMPTY, + val mainDesc: String = "", + val varsDesc: String = "", + val actionsList: List = listOf(), + val objectsList: List = listOf(), + val menuItemsList: List = listOf() ) : Parcelable { - constructor(source: Parcel) : this ( + constructor(source: Parcel) : this( source.readParcelable(LibIConfig::class.java.classLoader) ?: LibIConfig(), source.readInt() != 0, source.readLong(), @@ -52,20 +52,6 @@ data class LibGameState( override fun describeContents(): Int = 0 - fun reset() { - interfaceConfig.reset() - gameRunning = false - gameId = 0L - gameTitle = "" - gameDirUri = Uri.EMPTY - gameFileUri = Uri.EMPTY - mainDesc = "" - varsDesc = "" - actionsList.clear() - objectsList.clear() - menuItemsList.clear() - } - companion object CREATOR : Parcelable.Creator { override fun createFromParcel(source: Parcel): LibGameState { return LibGameState(source) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt index a304c40..960d1ee 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt @@ -4,11 +4,11 @@ import android.os.Parcel import android.os.Parcelable data class LibIConfig( - var useHtml: Boolean = false, - var fontSize: Long = 0L, - var backColor: Long = 0L, - var fontColor: Long = 0L, - var linkColor: Long = 0L + val useHtml: Boolean = false, + val fontSize: Long = 0L, + val backColor: Long = 0L, + val fontColor: Long = 0L, + val linkColor: Long = 0L ) : Parcelable { constructor(source: Parcel) : this( @@ -19,14 +19,6 @@ data class LibIConfig( source.readLong() ) - fun reset() { - useHtml = false - fontSize = 0L - backColor = 0L - fontColor = 0L - linkColor = 0L - } - override fun describeContents(): Int = 0 override fun writeToParcel(dest: Parcel, flags: Int) { diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt index 33371ec..8680082 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt @@ -4,11 +4,11 @@ import android.os.Parcel import android.os.Parcelable data class LibRefIRequest( - var isIConfigChanged: Boolean = false, - var isMainDescChanged: Boolean = false, - var isActionsChanged: Boolean = false, - var isObjectsChanged: Boolean = false, - var isVarsDescChanged: Boolean = false + val isIConfigChanged: Boolean = false, + val isMainDescChanged: Boolean = false, + val isActionsChanged: Boolean = false, + val isObjectsChanged: Boolean = false, + val isVarsDescChanged: Boolean = false ) : Parcelable { constructor(source: Parcel) : this( From 409e0373b1fc0f89f70e6817d9d216273c6f3f96 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 7 Mar 2025 07:10:28 +0300 Subject: [PATCH 25/57] Refactor `impl` package * Added support for immutable data classes * Backported the changes to the array build logic from 3.25 * Improved the names of some native methods --- .../lib/impl/LibAlphaProxyImpl.kt | 204 +++++++------- .../lib/impl/LibBravoProxyImpl.kt | 198 ++++++------- .../lib/impl/LibCharlieProxyImpl.kt | 260 +++++++++--------- .../src/bindings/android/android_control.c | 40 +-- .../main/java/org/libsnxqsp/jni/SNXLib.java | 40 +-- 5 files changed, 372 insertions(+), 370 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 7686cae..08ff333 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -8,6 +8,7 @@ import android.os.SystemClock import android.util.Log import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.file.DocumentFileCompat.fromUri +import com.anggrayudi.storage.file.child import com.libqsp.jni.QSPLib import org.qp.android.questopiabundle.GameInterface import org.qp.android.questopiabundle.dto.LibListItem @@ -17,25 +18,23 @@ import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog import org.qp.android.questopiabundle.lib.LibTypeWindow import org.qp.android.questopiabundle.utils.FileUtil.fromFullPath -import org.qp.android.questopiabundle.utils.FileUtil.fromRelPath import org.qp.android.questopiabundle.utils.FileUtil.getFileContents +import org.qp.android.questopiabundle.utils.FileUtil.isWritableDir import org.qp.android.questopiabundle.utils.FileUtil.isWritableFile import org.qp.android.questopiabundle.utils.FileUtil.writeFileContents import org.qp.android.questopiabundle.utils.HtmlUtil.getSrcDir import org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags -import org.qp.android.questopiabundle.utils.HtmlUtil.removeHtmlTags import org.qp.android.questopiabundle.utils.PathUtil.getFilename import org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath import org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread -import java.util.* import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.Volatile class LibAlphaProxyImpl( private val context: Context, - override val gameState: LibGameState = LibGameState() + override var gameState: LibGameState ) : QSPLib(), LibIProxy { private val TAG = javaClass.simpleName @@ -81,16 +80,14 @@ class LibAlphaProxyImpl( val errorData = lastErrorData val locName = getStringOrEmpty(errorData.locName) val desc = getStringOrEmpty(getErrorDesc(errorData.errorNum)) - val message = String.format( - Locale.getDefault(), - "Location: %s\nAction: %d\nLine: %d\nError number: %d\nDescription: %s", - locName, - errorData.actIndex, - errorData.intLineNum, - errorData.errorNum, - desc - ) - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, message) + + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, """ + Location: $locName + Action: ${errorData.actIndex} + Line: ${errorData.intLineNum} + Error number: ${errorData.errorNum} + Description: $desc + """.trimIndent()) } /** @@ -105,91 +102,107 @@ class LibAlphaProxyImpl( val htmlResult = getNumVarValue("USEHTML", 0) val useHtml = htmlResult != 0L if (config.useHtml != useHtml) { - config.useHtml = useHtml + gameState = gameState.copy( + interfaceConfig = config.copy( + useHtml = useHtml + ) + ) changed = true } val fSizeResult = getNumVarValue("FSIZE", 0) if (config.fontSize != fSizeResult) { - config.fontSize = fSizeResult + gameState = gameState.copy( + interfaceConfig = config.copy( + fontSize = fSizeResult + ) + ) changed = true } val bColorResult = getNumVarValue("BCOLOR", 0) if (config.backColor != bColorResult) { - config.backColor = bColorResult + gameState = gameState.copy( + interfaceConfig = config.copy( + backColor = bColorResult + ) + ) changed = true } val fColorResult = getNumVarValue("FCOLOR", 0) if (config.fontColor != fColorResult) { - config.fontColor = fColorResult + gameState = gameState.copy( + interfaceConfig = config.copy( + fontColor = fColorResult + ) + ) changed = true } val lColorResult = getNumVarValue("LCOLOR", 0) if (config.linkColor != lColorResult) { - config.linkColor = lColorResult + gameState = gameState.copy( + interfaceConfig = config.copy( + linkColor = lColorResult + ) + ) changed = true } return changed } - private val actionsList: ArrayList + private val actionsList: List get() { - val actions = ArrayList() - val currGameDir = currGameDir + if (!isWritableDir(context, currGameDir)) return emptyList() + val actions = mutableListOf() + val gameDir = currGameDir for (element in getActions()) { - val newElement = LibListItem(element) - if (isNotEmptyOrBlank(newElement.pathToImage) && currGameDir != null) { - val tempPath = - normalizeContentPath(getFilename(newElement.pathToImage)) - val fileFromPath = - fromRelPath(context, tempPath, currGameDir, false) - if (fileFromPath != null) { - newElement.pathToImage = fileFromPath.uri.toString() - } else { - newElement.pathToImage = "" + var tempImagePath = element.image + val tempText = element.name + + if (isNotEmptyOrBlank(tempImagePath)) { + val tempPath = normalizeContentPath(getFilename(tempImagePath)) + val fileFromPath = gameDir?.child(context, tempPath) + if (isWritableFile(context, fileFromPath)) { + tempImagePath = fileFromPath?.uri.toString() } } - newElement.text = if (gameState.interfaceConfig.useHtml) { - removeHtmlTags(newElement.text) - } else { - newElement.text - } - actions.add(newElement) + + actions.add(LibListItem(tempText, tempImagePath)) } return actions } - private val objectsList: ArrayList + private val objectsList: List get() { - val objects = ArrayList() - val currGameDir = currGameDir + if (!isWritableDir(context, currGameDir)) return emptyList() + val objects = mutableListOf() + val gameDir = currGameDir for (element in getObjects()) { - val newElement = LibListItem(element) - if (newElement.text.contains(" + private val actionsList: List get() { - val actions = ArrayList() - val currGameDir = currGameDir + if (!isWritableDir(context, currGameDir)) return emptyList() + val actions = mutableListOf() + val gameDir = currGameDir for (element in QSPGetActionData()) { - val newElement = LibListItem(element) - if (isNotEmptyOrBlank(newElement.pathToImage) && currGameDir != null) { - val tempPath = - normalizeContentPath(getFilename(newElement.pathToImage)) - val fileFromPath = - fromRelPath(context, tempPath, currGameDir, false) - if (fileFromPath != null) { - newElement.pathToImage = fileFromPath.uri.toString() - } else { - newElement.pathToImage = "" + var tempImagePath = element.image + val tempText = element.text + + if (isNotEmptyOrBlank(tempImagePath)) { + val tempPath = normalizeContentPath(getFilename(tempImagePath)) + val fileFromPath = gameDir?.child(context, tempPath) + if (isWritableFile(context, fileFromPath)) { + tempImagePath = fileFromPath?.uri.toString() } } - newElement.text = if (gameState.interfaceConfig.useHtml) { - removeHtmlTags(newElement.text) - } else { - newElement.text - } - actions.add(newElement) + + actions.add(LibListItem(tempText, tempImagePath)) } return actions } - private val objectsList: ArrayList + private val objectsList: List get() { - val objects = ArrayList() - val currGameDir = currGameDir + if (!isWritableDir(context, currGameDir)) return emptyList() + val objects = mutableListOf() + val gameDir = currGameDir for (element in QSPGetObjectData()) { - val newElement = LibListItem(element) - if (newElement.text.contains(" + private val actionsList: List get() { - val actions = ArrayList() - val currGameDir = currGameDir + if (!isWritableDir(context, currGameDir)) return emptyList() + val actions = mutableListOf() + val gameDir = currGameDir for (element in getActions()) { - val newElement = LibListItem(element) - if (isNotEmptyOrBlank(newElement.pathToImage) && currGameDir != null) { - val tempPath = - normalizeContentPath(getFilename(newElement.pathToImage)) - val fileFromPath = - fromRelPath(context, tempPath, currGameDir, false) - if (fileFromPath != null) { - newElement.pathToImage = fileFromPath.uri.toString() - } else { - newElement.pathToImage = "" + var tempImagePath = element.image + val tempText = element.text + + if (isNotEmptyOrBlank(tempImagePath)) { + val tempPath = normalizeContentPath(getFilename(tempImagePath)) + val fileFromPath = gameDir?.child(context, tempPath) + if (isWritableFile(context, fileFromPath)) { + tempImagePath = fileFromPath?.uri.toString() } } - newElement.text = if (gameState.interfaceConfig.useHtml) { - removeHtmlTags(newElement.text) - } else { - newElement.text - } - actions.add(newElement) + + actions.add(LibListItem(tempText, tempImagePath)) } return actions } - private val objectsList: ArrayList + private val objectsList: List get() { - val objects = ArrayList() - val currGameDir = currGameDir + if (!isWritableDir(context, currGameDir)) return emptyList() + val objects = mutableListOf() + val gameDir = currGameDir for (element in getObjects()) { - val newElement = LibListItem(element) - if (newElement.text.contains("NewGlobalRef(env, clazz); /* Get references to callbacks */ - qspSetCallBack(QSP_CALL_DEBUG, (*env)->GetMethodID(env, snxApiClass, "CallDebug", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_ISPLAYINGFILE, (*env)->GetMethodID(env, snxApiClass, "IsPlayingFile", "(Ljava/lang/String;)Z")); - qspSetCallBack(QSP_CALL_PLAYFILE, (*env)->GetMethodID(env, snxApiClass, "PlayFile", "(Ljava/lang/String;I)V")); - qspSetCallBack(QSP_CALL_CLOSEFILE, (*env)->GetMethodID(env, snxApiClass, "CloseFile", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_SHOWIMAGE, (*env)->GetMethodID(env, snxApiClass, "ShowPicture", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_SHOWWINDOW, (*env)->GetMethodID(env, snxApiClass, "ShowWindow", "(IZ)V")); - qspSetCallBack(QSP_CALL_SHOWMENU, (*env)->GetMethodID(env, snxApiClass, "showMenu", "()I")); - qspSetCallBack(QSP_CALL_SHOWMSGSTR, (*env)->GetMethodID(env, snxApiClass, "ShowMessage", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_REFRESHINT, (*env)->GetMethodID(env, snxApiClass, "RefreshInt", "()V")); - qspSetCallBack(QSP_CALL_SETTIMER, (*env)->GetMethodID(env, snxApiClass, "SetTimer", "(I)V")); + qspSetCallBack(QSP_CALL_DEBUG, (*env)->GetMethodID(env, snxApiClass, "onDebug", "(Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_ISPLAYINGFILE, (*env)->GetMethodID(env, snxApiClass, "onIsPlayingFile", "(Ljava/lang/String;)Z")); + qspSetCallBack(QSP_CALL_PLAYFILE, (*env)->GetMethodID(env, snxApiClass, "onPlayFile", "(Ljava/lang/String;I)V")); + qspSetCallBack(QSP_CALL_CLOSEFILE, (*env)->GetMethodID(env, snxApiClass, "onCloseFile", "(Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_SHOWIMAGE, (*env)->GetMethodID(env, snxApiClass, "onShowImage", "(Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_SHOWWINDOW, (*env)->GetMethodID(env, snxApiClass, "onShowWindow", "(IZ)V")); + qspSetCallBack(QSP_CALL_SHOWMENU, (*env)->GetMethodID(env, snxApiClass, "onShowMenu", "()I")); + qspSetCallBack(QSP_CALL_SHOWMSGSTR, (*env)->GetMethodID(env, snxApiClass, "onShowMessage", "(Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_REFRESHINT, (*env)->GetMethodID(env, snxApiClass, "onRefreshInt", "()V")); + qspSetCallBack(QSP_CALL_SETTIMER, (*env)->GetMethodID(env, snxApiClass, "onSetTimer", "(I)V")); qspSetCallBack(QSP_CALL_SYSTEM, (*env)->GetMethodID(env, snxApiClass, "onSystem", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_SETINPUTSTRTEXT, (*env)->GetMethodID(env, snxApiClass, "setInputStrText", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_DELETEMENU, (*env)->GetMethodID(env, snxApiClass, "deleteMenu", "()V")); - qspSetCallBack(QSP_CALL_OPENGAMESTATUS, (*env)->GetMethodID(env, snxApiClass, "OpenGame", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_SAVEGAMESTATUS, (*env)->GetMethodID(env, snxApiClass, "SaveGame", "(Ljava/lang/String;)V")); - qspSetCallBack(QSP_CALL_SLEEP, (*env)->GetMethodID(env, snxApiClass, "Wait", "(I)V")); - qspSetCallBack(QSP_CALL_GETMSCOUNT, (*env)->GetMethodID(env, snxApiClass, "GetMSCount", "()I")); - qspSetCallBack(QSP_CALL_INPUTBOX, (*env)->GetMethodID(env, snxApiClass, "InputBox", "(Ljava/lang/String;)Ljava/lang/String;")); - qspSetCallBack(QSP_CALL_ADDMENUITEM, (*env)->GetMethodID(env, snxApiClass, "addMenuItem", "(Ljava/lang/String;Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_SETINPUTSTRTEXT, (*env)->GetMethodID(env, snxApiClass, "onSetInputStrText", "(Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_DELETEMENU, (*env)->GetMethodID(env, snxApiClass, "onDeleteMenu", "()V")); + qspSetCallBack(QSP_CALL_OPENGAMESTATUS, (*env)->GetMethodID(env, snxApiClass, "onOpenGame", "(Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_SAVEGAMESTATUS, (*env)->GetMethodID(env, snxApiClass, "onSaveGame", "(Ljava/lang/String;)V")); + qspSetCallBack(QSP_CALL_SLEEP, (*env)->GetMethodID(env, snxApiClass, "onSleep", "(I)V")); + qspSetCallBack(QSP_CALL_GETMSCOUNT, (*env)->GetMethodID(env, snxApiClass, "onGetMsCount", "()I")); + qspSetCallBack(QSP_CALL_INPUTBOX, (*env)->GetMethodID(env, snxApiClass, "onInputBox", "(Ljava/lang/String;)Ljava/lang/String;")); + qspSetCallBack(QSP_CALL_ADDMENUITEM, (*env)->GetMethodID(env, snxApiClass, "onAddMenuItem", "(Ljava/lang/String;Ljava/lang/String;)V")); } /* Deinitialization */ diff --git a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java index b9c3051..f06433b 100644 --- a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java +++ b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java @@ -27,9 +27,9 @@ public record ErrorData(String locName, int errorNum, int index, int line) { } public native Object QSPGetExprValue();//!!!STUB /* Main desc */ public native String getMainDesc(); - public native boolean QSPIsMainDescChanged(); + public native boolean isMainDescChanged(); /* Vars desc */ - public native String QSPGetVarsDesc(); + public native String getVarsDesc(); public native boolean isVarsDescChanged(); public native int getVarValuesCount(String name); public native Object QSPGetVarValues(String name, int ind);//!!!STUB @@ -68,22 +68,22 @@ public record ErrorData(String locName, int errorNum, int index, int line) { } public native boolean QSPRestartGame(boolean isRefresh); public void onSystem(String code) { } - public void CallDebug(String str) { } - public void RefreshInt() { } - public void ShowPicture(String path) { } - public void SetTimer(int msecs) { } - public void setInputStrText(String text) { } - public void ShowMessage(String message) { } - public void PlayFile(String path, int volume) { } - public boolean IsPlayingFile(final String path) { return false; } - public void CloseFile(String path) { } - public void OpenGame(String filename) { } - public void SaveGame(String filename) { } - public String InputBox(String prompt) { return null; } - public int GetMSCount() { return 0; } - public void addMenuItem(String name, String imgPath) { } - public int showMenu() { return -1; } - public void deleteMenu() { } - public void Wait(int msecs) { } - public void ShowWindow(int type, boolean isShow) { } + public void onDebug(String str) { } + public void onRefreshInt() { } + public void onShowImage(String path) { } + public void onSetTimer(int msecs) { } + public void onSetInputStrText(String text) { } + public void onShowMessage(String message) { } + public void onPlayFile(String path, int volume) { } + public boolean onIsPlayingFile(final String path) { return false; } + public void onCloseFile(String path) { } + public void onOpenGame(String filename) { } + public void onSaveGame(String filename) { } + public String onInputBox(String prompt) { return null; } + public int onGetMsCount() { return 0; } + public void onAddMenuItem(String name, String imgPath) { } + public int onShowMenu() { return -1; } + public void onDeleteMenu() { } + public void onSleep(int msecs) { } + public void onShowWindow(int type, boolean isShow) { } } \ No newline at end of file From 780f0a78089a902e8ab0edee5072104e03bf5249 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:19:01 +0300 Subject: [PATCH 26/57] Add a default value for the gameState field --- .../qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 08ff333..70c5c85 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -34,7 +34,7 @@ import kotlin.concurrent.Volatile class LibAlphaProxyImpl( private val context: Context, - override var gameState: LibGameState + override var gameState: LibGameState = LibGameState() ) : QSPLib(), LibIProxy { private val TAG = javaClass.simpleName From ef22b6f0bae3403beeaeb62bd9d82aaf82105e4c Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:20:58 +0300 Subject: [PATCH 27/57] Secure native methods --- .../questopiabundle/lib/impl/LibBravoProxyImpl.kt | 12 ++++++------ .../questopiabundle/lib/impl/LibCharlieProxyImpl.kt | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 2aea844..edaa40b 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -172,8 +172,8 @@ class LibBravoProxyImpl( val gameDir = currGameDir for (element in QSPGetActionData()) { - var tempImagePath = element.image - val tempText = element.text + var tempImagePath = element.image ?: "" + val tempText = element.text ?: "" if (isNotEmptyOrBlank(tempImagePath)) { val tempPath = normalizeContentPath(getFilename(tempImagePath)) @@ -196,8 +196,8 @@ class LibBravoProxyImpl( val gameDir = currGameDir for (element in QSPGetObjectData()) { - var tempImagePath = element.image - val tempText = element.text + var tempImagePath = element.image ?: "" + val tempText = element.text ?: "" if (tempText.contains(" Date: Sun, 9 Mar 2025 00:21:39 +0300 Subject: [PATCH 28/57] Make the data class immutable --- .../qp/android/questopiabundle/LibDialogRetValue.kt | 12 ++++++------ .../qp/android/questopiabundle/QuestopiaBundle.kt | 11 +---------- .../qp/android/questopiabundle/dto/LibMenuItem.kt | 4 ++-- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt index 686cd7b..00dc3e9 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt @@ -4,14 +4,14 @@ import android.os.Parcel import android.os.Parcelable data class LibDialogRetValue( - var outTextValue: String = "", - var outNumValue: Int = 0 + val outTextValue: String = "", + val outNumValue: Int = 0 ) : Parcelable { - constructor(source: Parcel) : this() { - outTextValue = source.readString() ?: "" - outNumValue = source.readInt() - } + constructor(source: Parcel) : this( + source.readString() ?: "", + source.readInt() + ) override fun describeContents(): Int = 0 diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index d04a374..fe9b4d5 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -148,21 +148,12 @@ class QuestopiaBundle : Service(), GameInterface { } override fun showLibDialog(dialog: LibTypeDialog?, inputString: String?): LibDialogRetValue? { - val wrap = LibDialogRetValue() - try { return callbacks?.doOnShowDialog(LibResult(dialog), inputString) } catch (e: RemoteException) { Log.e("QuestopiaBundle", "Error", e) + return LibDialogRetValue() } - - if (dialog == LibTypeDialog.DIALOG_MENU) { - wrap.outNumValue = -1 - } else { - wrap.outTextValue = "" - } - - return wrap } override fun changeVisWindow(type: LibTypeWindow?, show: Boolean) { diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt index bcd1929..141e0a9 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt @@ -4,8 +4,8 @@ import android.os.Parcel import android.os.Parcelable data class LibMenuItem( - var name: String = "", - var pathToImage: String = "" + val name: String = "", + val pathToImage: String = "" ) : Parcelable { constructor(source: Parcel) : this( From 799d28c99a47095f3fbb4d6c19f433afff6e2985 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:40:37 +0300 Subject: [PATCH 29/57] Rename .java to .kt --- .../src/main/java/org/libndkqsp/jni/{NDKLib.java => NDKLib.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libbravo/src/main/java/org/libndkqsp/jni/{NDKLib.java => NDKLib.kt} (100%) diff --git a/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt similarity index 100% rename from libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java rename to libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt From 6dfa3d37a1d3cac340625509acfb16b0cd1d7f92 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:40:38 +0300 Subject: [PATCH 30/57] Convert to Kotlin --- .../lib/impl/LibBravoProxyImpl.kt | 61 +++--- .../src/main/java/org/libndkqsp/jni/NDKLib.kt | 178 ++++++++++-------- 2 files changed, 136 insertions(+), 103 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index edaa40b..a4584ed 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -29,6 +29,7 @@ import org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags import org.qp.android.questopiabundle.utils.PathUtil.getFilename import org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath import org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty +import org.qp.android.questopiabundle.utils.StringUtil.isEmptyOrBlank import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread import java.util.concurrent.locks.ReentrantLock @@ -171,9 +172,10 @@ class LibBravoProxyImpl( val actions = mutableListOf() val gameDir = currGameDir - for (element in QSPGetActionData()) { - var tempImagePath = element.image ?: "" - val tempText = element.text ?: "" + for (element in QSPGetActionData() ?: return emptyList()) { + val safeElement = element ?: continue + var tempImagePath = safeElement.image ?: "" + val tempText = safeElement.text ?: "" if (isNotEmptyOrBlank(tempImagePath)) { val tempPath = normalizeContentPath(getFilename(tempImagePath)) @@ -195,7 +197,8 @@ class LibBravoProxyImpl( val objects = mutableListOf() val gameDir = currGameDir - for (element in QSPGetObjectData()) { + for (element in QSPGetObjectData() ?: return emptyList()) { + val safeElement = element ?: continue var tempImagePath = element.image ?: "" val tempText = element.text ?: "" @@ -409,32 +412,29 @@ class LibBravoProxyImpl( ) } - override fun ShowPicture(path: String) { - val inter = gameInterface + override fun ShowPicture(path: String?) { if (!isNotEmptyOrBlank(path)) return - inter.showLibDialog(LibTypeDialog.DIALOG_PICTURE, path) + gameInterface.showLibDialog(LibTypeDialog.DIALOG_PICTURE, path) } override fun SetTimer(msecs: Int) { - val inter = gameInterface - inter.setCountInter(msecs) + gameInterface.setCountInter(msecs) } - override fun ShowMessage(message: String) { - val inter = gameInterface - inter.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, message) + override fun ShowMessage(message: String?) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, message) } - override fun PlayFile(path: String, volume: Int) { + override fun PlayFile(path: String?, volume: Int) { if (!isNotEmptyOrBlank(path)) return gameInterface.playFile(path, volume) } - override fun IsPlayingFile(path: String): Boolean { - return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path) + override fun IsPlayingFile(path: String?): Boolean { + return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path.toString()) } - override fun CloseFile(path: String) { + override fun CloseFile(path: String?) { if (isNotEmptyOrBlank(path)) { gameInterface.closeFile(path) } else { @@ -443,11 +443,11 @@ class LibBravoProxyImpl( } override fun OpenGame(filename: String?) { - if (filename == null) { + if (!isNotEmptyOrBlank(filename)) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) } else { try { - val saveFile = fromFullPath(context, filename) ?: return + val saveFile = fromFullPath(context, filename.toString()) ?: return gameInterface.requestPermFile(saveFile.uri) if (isWritableFile(context, saveFile)) { gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } @@ -475,7 +475,7 @@ class LibBravoProxyImpl( } } - override fun InputBox(prompt: String): String { + override fun InputBox(prompt: String?): String { val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, prompt) ?: return "" return doShow.outTextValue } @@ -490,11 +490,11 @@ class LibBravoProxyImpl( return dt } - override fun AddMenuItem(name: String, imgPath: String) { - val item = LibMenuItem() - item.name = name - item.pathToImage = imgPath - gameState = gameState.copy(menuItemsList = listOf(item)) + override fun AddMenuItem(name: String?, imgPath: String?) { + gameState = gameState.copy(menuItemsList = listOf(LibMenuItem( + name ?: "", + imgPath ?: "" + ))) } override fun ShowMenu() { @@ -522,15 +522,18 @@ class LibBravoProxyImpl( gameInterface.changeVisWindow(windowType, isShow) } - override fun GetFileContents(path: String): ByteArray? { - val targetFile = fromFullPath(context, path) ?: return null + override fun GetFileContents(path: String?): ByteArray? { + if (isEmptyOrBlank(path)) return byteArrayOf() + val targetFile = fromFullPath(context, path.toString()) ?: return null val targetFileUri = targetFile.uri gameInterface.requestPermFile(targetFileUri) - return getFileContents(context, targetFileUri)!! + return getFileContents(context, targetFileUri) } - override fun ChangeQuestPath(path: String) { - val newGameDir = fromFullPath(context, path) + override fun ChangeQuestPath(path: String?) { + if (isEmptyOrBlank(path)) return + + val newGameDir = fromFullPath(context, path.toString()) if (newGameDir == null || !newGameDir.exists()) { Log.e(TAG, "Game directory not found: $path") return diff --git a/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt index 4e6560b..7aa73fd 100644 --- a/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt +++ b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt @@ -1,91 +1,121 @@ -package org.libndkqsp.jni; +package org.libndkqsp.jni -public abstract class NDKLib { +abstract class NDKLib { + @JvmRecord + data class ListItem(val image: String = "", val text: String = "") - public record ListItem(String image, String text) { } + @JvmRecord + data class ExecutionState(val loc: String, val actIndex: Int, val lineNum: Int) - public record ExecutionState(String loc, int actIndex, int lineNum) { } + @JvmRecord + data class VarValResp(val isSuccess: Boolean, val stringValue: String, val intValue: Int) - public record VarValResp(boolean isSuccess , String stringValue , int intValue) { } + @JvmRecord + data class ErrorData(val locName: String, val errorNum: Int, val index: Int, val line: Int) - public record ErrorData(String locName , int errorNum , int index , int line) { } + external fun QSPInit() + external fun QSPDeInit() + external fun QSPIsInCallBack(): Boolean + external fun QSPEnableDebugMode(isDebug: Boolean) + external fun QSPGetCurStateData(): Any? //!!!STUB + external fun QSPGetVersion(): String? + external fun QSPGetCompiledDateTime(): String? + external fun QSPGetFullRefreshCount(): Int + external fun QSPGetQstFullPath(): String? + external fun QSPGetCurLoc(): String? + external fun QSPGetExprValue(): Any? //!!!STUB - static { - System.loadLibrary("ndkqsp"); - } - - public native void QSPInit(); - public native void QSPDeInit(); - public native boolean QSPIsInCallBack(); - public native void QSPEnableDebugMode(boolean isDebug); - public native Object QSPGetCurStateData();//!!!STUB - public native String QSPGetVersion(); - public native String QSPGetCompiledDateTime(); - public native int QSPGetFullRefreshCount(); - public native String QSPGetQstFullPath(); - public native String QSPGetCurLoc(); - public native Object QSPGetExprValue();//!!!STUB /* Main desc */ - public native String QSPGetMainDesc(); - public native boolean QSPIsMainDescChanged(); + external fun QSPGetMainDesc(): String? + external fun QSPIsMainDescChanged(): Boolean + /* Vars desc */ - public native String QSPGetVarsDesc(); - public native boolean QSPIsVarsDescChanged(); - public native Object QSPGetVarValuesCount(String name); - public native Object QSPGetVarValues(String name, int ind);//!!!STUB - public native int QSPGetMaxVarsCount(); - public native Object QSPGetVarNameByIndex(int index);//!!!STUB + external fun QSPGetVarsDesc(): String? + external fun QSPIsVarsDescChanged(): Boolean + external fun QSPGetVarValuesCount(name: String?): Any? + external fun QSPGetVarValues(name: String?, ind: Int): Any? //!!!STUB + external fun QSPGetMaxVarsCount(): Int + external fun QSPGetVarNameByIndex(index: Int): Any? //!!!STUB + /* Input string */ - public native void QSPSetInputStrText(String val); + external fun QSPSetInputStrText(`val`: String?) + /* Actions */ - public native int QSPGetActionsCount(); - public native ListItem[] QSPGetActionData(); - public native boolean QSPExecuteSelActionCode(boolean isRefresh); - public native boolean QSPSetSelActionIndex(int ind, boolean isRefresh); - public native int QSPGetSelActionIndex(); - public native boolean QSPIsActionsChanged(); + external fun QSPGetActionsCount(): Int + external fun QSPGetActionData(): List? + external fun QSPExecuteSelActionCode(isRefresh: Boolean): Boolean + external fun QSPSetSelActionIndex(ind: Int, isRefresh: Boolean): Boolean + external fun QSPGetSelActionIndex(): Int + external fun QSPIsActionsChanged(): Boolean + /* Objects */ - public native ListItem[] QSPGetObjectData(); - public native int QSPGetObjectsCount(); - public native boolean QSPSetSelObjectIndex(int ind, boolean isRefresh); - public native int QSPGetSelObjectIndex(); - public native boolean QSPIsObjectsChanged(); + external fun QSPGetObjectData(): List? + external fun QSPGetObjectsCount(): Int + external fun QSPSetSelObjectIndex(ind: Int, isRefresh: Boolean): Boolean + external fun QSPGetSelObjectIndex(): Int + external fun QSPIsObjectsChanged(): Boolean + /* Code execution */ - public native boolean QSPExecString(String s, boolean isRefresh); - public native boolean QSPExecLocationCode(String name, boolean isRefresh); - public native boolean QSPExecCounter(boolean isRefresh); - public native boolean QSPExecUserInput(boolean isRefresh); + external fun QSPExecString(s: String?, isRefresh: Boolean): Boolean + external fun QSPExecLocationCode(name: String?, isRefresh: Boolean): Boolean + external fun QSPExecCounter(isRefresh: Boolean): Boolean + external fun QSPExecUserInput(isRefresh: Boolean): Boolean + /* Errors */ - public native ErrorData QSPGetLastErrorData(); - public native String QSPGetErrorDesc(int errorNum); + external fun QSPGetLastErrorData(): ErrorData? + external fun QSPGetErrorDesc(errorNum: Int): String? + /* Game */ - public native boolean QSPLoadGameWorld(String fileName); - public native boolean QSPLoadGameWorldFromData(byte[] data, String fileName); - public native boolean QSPSaveGame(String fileName, boolean isRefresh); - public native byte[] QSPSaveGameAsData(boolean isRefresh); - public native boolean QSPOpenSavedGame(String fileName, boolean isRefresh); - public native boolean QSPOpenSavedGameFromData(byte[] data , int dataSize, boolean isRefresh); - public native boolean QSPRestartGame(boolean isRefresh); - public native void QSPSelectMenuItem(int index); + external fun QSPLoadGameWorld(fileName: String?): Boolean + external fun QSPLoadGameWorldFromData(data: ByteArray?, fileName: String?): Boolean + external fun QSPSaveGame(fileName: String?, isRefresh: Boolean): Boolean + external fun QSPSaveGameAsData(isRefresh: Boolean): ByteArray? + external fun QSPOpenSavedGame(fileName: String?, isRefresh: Boolean): Boolean + external fun QSPOpenSavedGameFromData( + data: ByteArray?, + dataSize: Int, + isRefresh: Boolean + ): Boolean + + external fun QSPRestartGame(isRefresh: Boolean): Boolean + external fun QSPSelectMenuItem(index: Int) + //public native void QSPSetCallBack(int type, QSP_CALLBACK func) + fun CallDebug(str: String?) {} + open fun RefreshInt() {} + open fun ShowPicture(path: String?) {} + open fun SetTimer(msecs: Int) {} + open fun ShowMessage(message: String?) {} + open fun PlayFile(path: String?, volume: Int) {} + open fun IsPlayingFile(path: String?): Boolean { + return false + } + + open fun CloseFile(path: String?) {} + open fun OpenGame(filename: String?) {} + open fun SaveGame(filename: String?) {} + open fun InputBox(prompt: String?): String? { + return null + } + + open fun GetMSCount(): Int { + return 0 + } + + open fun AddMenuItem(name: String?, imgPath: String?) {} + open fun ShowMenu() {} + open fun DeleteMenu() {} + open fun Wait(msecs: Int) {} + open fun ShowWindow(type: Int, isShow: Boolean) {} + open fun GetFileContents(path: String?): ByteArray? { + return null + } - public void CallDebug(String str) {} - public void RefreshInt() { } - public void ShowPicture(String path) { } - public void SetTimer(int msecs) { } - public void ShowMessage(String message) { } - public void PlayFile(String path, int volume) { } - public boolean IsPlayingFile(final String path) { return false; } - public void CloseFile(String path) { } - public void OpenGame(String filename) { } - public void SaveGame(String filename) { } - public String InputBox(String prompt) { return null; } - public int GetMSCount() { return 0; } - public void AddMenuItem(String name, String imgPath) { } - public void ShowMenu() { } - public void DeleteMenu() { } - public void Wait(int msecs) { } - public void ShowWindow(int type, boolean isShow) { } - public byte[] GetFileContents(String path) { return null; } - public void ChangeQuestPath(String path) { } + open fun ChangeQuestPath(path: String?) {} + + companion object { + init { + System.loadLibrary("ndkqsp") + } + } } From 3e93b061922594a4aba88680a3d947946118ce91 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:42:59 +0300 Subject: [PATCH 31/57] Fix the creation of the menu list --- .../questopiabundle/lib/impl/LibCharlieProxyImpl.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index 33ab586..be222f4 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -484,10 +484,10 @@ class LibCharlieProxyImpl( } override fun onAddMenuItem(name: String?, imgPath: String?) { - val item = LibMenuItem() - item.name = name ?: "" - item.pathToImage = imgPath ?: "" - gameState = gameState.copy(menuItemsList = listOf(item)) + gameState = gameState.copy(menuItemsList = listOf(LibMenuItem( + name ?: "", + imgPath ?: "" + ))) } override fun onShowMenu(): Int { From 3ab5990e7565930a7e902f9456cb6acb3e3e66d2 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 05:15:40 +0300 Subject: [PATCH 32/57] Refactor `loadInterfaceConfiguration` method --- .../lib/impl/LibAlphaProxyImpl.kt | 65 +++++-------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 70c5c85..9358745 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -96,61 +96,28 @@ class LibAlphaProxyImpl( * @return `true` if the configuration has changed, otherwise `false` */ private fun loadInterfaceConfiguration(): Boolean { - val config = gameState.interfaceConfig - var changed = false - val htmlResult = getNumVarValue("USEHTML", 0) - val useHtml = htmlResult != 0L - if (config.useHtml != useHtml) { - gameState = gameState.copy( - interfaceConfig = config.copy( - useHtml = useHtml - ) - ) - changed = true - } - val fSizeResult = getNumVarValue("FSIZE", 0) - if (config.fontSize != fSizeResult) { - gameState = gameState.copy( - interfaceConfig = config.copy( - fontSize = fSizeResult - ) - ) - changed = true - } - val bColorResult = getNumVarValue("BCOLOR", 0) - if (config.backColor != bColorResult) { - gameState = gameState.copy( - interfaceConfig = config.copy( - backColor = bColorResult - ) - ) - changed = true - } - val fColorResult = getNumVarValue("FCOLOR", 0) - if (config.fontColor != fColorResult) { - gameState = gameState.copy( - interfaceConfig = config.copy( - fontColor = fColorResult - ) - ) - changed = true - } - val lColorResult = getNumVarValue("LCOLOR", 0) - if (config.linkColor != lColorResult) { - gameState = gameState.copy( - interfaceConfig = config.copy( - linkColor = lColorResult - ) - ) - changed = true - } - return changed + val useHtml = htmlResult != 0L + val newConfig = gameState.interfaceConfig.copy( + useHtml = useHtml, + fontSize = fSizeResult, + backColor = bColorResult, + fontColor = fColorResult, + linkColor = lColorResult + ) + + return when { + newConfig != gameState.interfaceConfig -> { + gameState = gameState.copy(interfaceConfig = newConfig) + true + } + else -> false + } } private val actionsList: List From 005bda7b1ce5616ecc2ba9292238b06767f1b1bd Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 05:21:14 +0300 Subject: [PATCH 33/57] Improve the native binding --- .../lib/impl/LibCharlieProxyImpl.kt | 20 ++-- .../src/bindings/android/android_control.c | 104 ++---------------- .../main/java/org/libsnxqsp/jni/SNXLib.java | 20 ++-- 3 files changed, 29 insertions(+), 115 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index be222f4..36af010 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -74,7 +74,7 @@ class LibCharlieProxyImpl( val gameFileFullPath = documentWrap(gameFile!!).getAbsolutePath(context) val gameData = getFileContents(context, gameFileUri) ?: return false - if (!loadGameWorldFromData(gameData, gameData.size, gameFileFullPath)) { + if (!loadGameWorldFromData(gameData, gameFileFullPath)) { showLastQspError() return false } @@ -105,7 +105,7 @@ class LibCharlieProxyImpl( val config = gameState.interfaceConfig var changed = false - val htmlResult = QSPGetVarValues("USEHTML", 0) as VarValResp + val htmlResult = getVarValues("USEHTML", 0) if (htmlResult.isSuccess) { val useHtml = htmlResult.intValue != 0 if (config.useHtml != useHtml) { @@ -118,7 +118,7 @@ class LibCharlieProxyImpl( } } - val fSizeResult = QSPGetVarValues("FSIZE", 0) as VarValResp + val fSizeResult = getVarValues("FSIZE", 0) if (fSizeResult.isSuccess && config.fontSize != fSizeResult.intValue.toLong()) { gameState = gameState.copy( interfaceConfig = config.copy( @@ -128,7 +128,7 @@ class LibCharlieProxyImpl( changed = true } - val bColorResult = QSPGetVarValues("BCOLOR", 0) as VarValResp + val bColorResult = getVarValues("BCOLOR", 0) if (bColorResult.isSuccess && config.backColor != bColorResult.intValue.toLong()) { gameState = gameState.copy( interfaceConfig = config.copy( @@ -138,7 +138,7 @@ class LibCharlieProxyImpl( changed = true } - val fColorResult = QSPGetVarValues("FCOLOR", 0) as VarValResp + val fColorResult = getVarValues("FCOLOR", 0) if (fColorResult.isSuccess && config.fontColor != fColorResult.intValue.toLong()) { gameState = gameState.copy( interfaceConfig = config.copy( @@ -148,7 +148,7 @@ class LibCharlieProxyImpl( changed = true } - val lColorResult = QSPGetVarValues("LCOLOR", 0) as VarValResp + val lColorResult = getVarValues("LCOLOR", 0) if (lColorResult.isSuccess && config.linkColor != lColorResult.intValue.toLong()) { gameState = gameState.copy( interfaceConfig = config.copy( @@ -288,7 +288,7 @@ class LibCharlieProxyImpl( if (!loadGameWorld()) return@doWithCounterDisabled gameStartTime = SystemClock.elapsedRealtime() lastMsCountCallTime = 0 - if (!QSPRestartGame(true)) { + if (!restartGame(true)) { showLastQspError() } } @@ -302,7 +302,7 @@ class LibCharlieProxyImpl( gameInterface.requestPermFile(uri) val gameData = getFileContents(context, uri) ?: return - if (!QSPOpenSavedGameFromData(gameData, gameData.size, true)) { + if (!openSavedGameFromData(gameData, true)) { showLastQspError() } } @@ -314,7 +314,7 @@ class LibCharlieProxyImpl( } gameInterface.requestPermFile(uri) - val gameData = QSPSaveGameAsData(false) ?: return + val gameData = saveGameAsData(false) ?: return writeFileContents(context, uri, gameData) } @@ -343,7 +343,7 @@ class LibCharlieProxyImpl( gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, "userInputTitle") ?: return@runOnQspThread val input = doShow.outTextValue - QSPSetInputStrText(input) + setInputStrText(input) if (!execUserInput(true)) { showLastQspError() } diff --git a/libcharlie/src/main/cpp/src/bindings/android/android_control.c b/libcharlie/src/main/cpp/src/bindings/android/android_control.c index 8153f1a..432d90c 100644 --- a/libcharlie/src/main/cpp/src/bindings/android/android_control.c +++ b/libcharlie/src/main/cpp/src/bindings/android/android_control.c @@ -45,11 +45,6 @@ jclass snxExecutionStateClass; jclass snxErrorInfoClass; jclass snxVarValResp; -/* ------------------------------------------------------------ */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPIsInCallBack(JNIEnv *env, jobject this) -{ - return qspIsInCallBack; -} /* ------------------------------------------------------------ */ /* Debugging */ @@ -168,26 +163,14 @@ JNIEXPORT jobject JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetExprValue(JNIEnv * } /* ------------------------------------------------------------ */ /* Text of the input line */ -JNIEXPORT void JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPSetInputStrText(JNIEnv *env, jobject this, jstring val) +JNIEXPORT void JNICALL Java_org_libsnxqsp_jni_SNXLib_setInputStrText(JNIEnv *env, jobject this, jstring val) { - const char *str = (*env)->GetStringUTFChars(env, val, NULL); - if (str == NULL) - return; - QSP_CHAR *strConverted = qspC2W(str); - + QSP_CHAR *strConverted = snxFromJavaString(env, val); qspCurInputLen = qspAddText(&strConverted, (QSP_CHAR *)val, 0, -1, QSP_FALSE); - - (*env)->ReleaseStringUTFChars(env, val, str); } /* ------------------------------------------------------------ */ /* List of actions */ -/* Number of actions */ -JNIEXPORT jint JNICALL Java_org_libsnxqsp_jni_SNXLib_getActionsCount(JNIEnv *env, jobject this) -{ - return qspCurActionsCount; -} - /* Data actions with the specified index */ JNIEXPORT jobjectArray JNICALL Java_org_libsnxqsp_jni_SNXLib_getActions(JNIEnv *env, jobject this) { @@ -247,12 +230,6 @@ JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_isActionsChanged(JNIEnv /* ------------------------------------------------------------ */ /* List of objects */ -/* Number of objects */ -JNIEXPORT jint JNICALL Java_org_libsnxqsp_jni_SNXLib_getObjectsCount(JNIEnv *env, jobject this) -{ - return qspCurObjectsCount; -} - /* Object data with the specified index */ JNIEXPORT jobjectArray JNICALL Java_org_libsnxqsp_jni_SNXLib_getObjects(JNIEnv *env, jobject this) { @@ -340,17 +317,14 @@ QSP_BOOL QSPGetVarValues(const QSP_CHAR *name, int ind, int *numVal, QSP_CHAR ** return JNI_TRUE; } -JNIEXPORT jobject JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetVarValues(JNIEnv *env, jobject this, jstring name, jint ind) +JNIEXPORT jobject JNICALL Java_org_libsnxqsp_jni_SNXLib_getVarValues(JNIEnv *env, jobject this, jstring name, jint ind) { //Convert array name to QSP string - const char *str = (*env)->GetStringUTFChars(env, name, NULL); - if (str == NULL) - return NULL; - QSP_CHAR *strConverted = qspC2W(str); + QSP_CHAR *strConverted = snxFromJavaString(env, name); //Call QSP function int numVal = 0; - char *strVal; + QSP_CHAR *strVal; QSP_BOOL result = QSPGetVarValues(strConverted, (int)ind, &numVal, &strVal); // If this class does not exist then return null. @@ -382,7 +356,6 @@ JNIEXPORT jobject JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetVarValues(JNIEnv * (*env)->SetBooleanField(env, obj, successFid, JNI_FALSE); } - (*env)->ReleaseStringUTFChars(env, name, str); return obj; } @@ -519,63 +492,8 @@ JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_getErrorDesc(JNIEnv *env /* ------------------------------------------------------------ */ /* Game Management */ -/* Working with files */ - -/* Downloading a new game from a file */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPLoadGameWorld(JNIEnv *env, jobject this, jstring fileName) -{ - const char *str = (*env)->GetStringUTFChars(env, fileName, NULL); - if (str == NULL) - return JNI_FALSE; - - if (qspIsExitOnError && qspErrorNum) return QSP_FALSE; - qspResetError(); - if (qspIsDisableCodeExec) return QSP_FALSE; - qspOpenQuest((QSP_CHAR *)str, QSP_FALSE); - if (qspErrorNum) return QSP_FALSE; - - (*env)->ReleaseStringUTFChars(env, fileName, str); - return QSP_TRUE; -} - -/* Saving the state to a file */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPSaveGame(JNIEnv *env, jobject this, jstring fileName, jboolean isRefresh) -{ - const char *str = (*env)->GetStringUTFChars(env, fileName, NULL); - if (str == NULL) - return JNI_FALSE; - - if (qspIsExitOnError && qspErrorNum) return QSP_FALSE; - qspPrepareExecution(); - if (qspIsDisableCodeExec) return QSP_FALSE; - qspSaveGameStatus((QSP_CHAR*)str); - if (qspErrorNum) return QSP_FALSE; - if ((QSP_BOOL)isRefresh) qspCallRefreshInt(QSP_FALSE); - - (*env)->ReleaseStringUTFChars(env, fileName, str); - return QSP_TRUE; -} - -/* Loading status from a file */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPOpenSavedGame(JNIEnv *env, jobject this, jstring fileName, jboolean isRefresh) -{ - const char *str = (*env)->GetStringUTFChars(env, fileName, NULL); - if (str == NULL) - return JNI_FALSE; - - if (qspIsExitOnError && qspErrorNum) return QSP_FALSE; - qspPrepareExecution(); - if (qspIsDisableCodeExec) return QSP_FALSE; - qspOpenGameStatus((QSP_CHAR*)str); - if (qspErrorNum) return QSP_FALSE; - if ((QSP_BOOL)isRefresh) qspCallRefreshInt(QSP_FALSE); - - (*env)->ReleaseStringUTFChars(env, fileName, str); - return QSP_TRUE; -} - /* Working with memory */ -// + /* Loading a new game from memory */ QSP_BOOL QSPLoadGameWorldFromData(const char *data, int dataSize, const QSP_CHAR *fileName) { @@ -591,9 +509,10 @@ QSP_BOOL QSPLoadGameWorldFromData(const char *data, int dataSize, const QSP_CHAR if (qspErrorNum) return QSP_FALSE; return QSP_TRUE; } -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_loadGameWorldFromData(JNIEnv *env, jobject this, jbyteArray data, jint dataSize, jstring fileName) +JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_loadGameWorldFromData(JNIEnv *env, jobject this, jbyteArray data, jstring fileName) { //converting data + jint dataSize = (*env)->GetArrayLength(env, data); jbyte *jbuf = malloc(dataSize * sizeof(jbyte)); if (jbuf == NULL) return JNI_FALSE; @@ -650,7 +569,7 @@ QSP_BOOL QSPSaveGameAsData(void **buf, int *realSize, QSP_BOOL isRefresh) if (isRefresh) qspCallRefreshInt(QSP_FALSE); return QSP_TRUE; } -JNIEXPORT jbyteArray JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPSaveGameAsData(JNIEnv *env, jobject this, jboolean isRefresh) +JNIEXPORT jbyteArray JNICALL Java_org_libsnxqsp_jni_SNXLib_saveGameAsData(JNIEnv *env, jobject this, jboolean isRefresh) { void *buffer = NULL; int bufferSize = 0; @@ -685,9 +604,10 @@ QSP_BOOL QSPOpenSavedGameFromData(const void *data, int dataSize, QSP_BOOL isRef if (isRefresh) qspCallRefreshInt(QSP_FALSE); return QSP_TRUE; } -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPOpenSavedGameFromData(JNIEnv *env, jobject this, jbyteArray data, jint dataSize, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_openSavedGameFromData(JNIEnv *env, jobject this, jbyteArray data, jboolean isRefresh) { //converting data + jint dataSize = (*env)->GetArrayLength(env, data); jbyte *jbuf = malloc(dataSize * sizeof(jbyte)); if (jbuf == NULL) return JNI_FALSE; @@ -715,7 +635,7 @@ QSP_BOOL QSPOpenSavedGameFromString(const QSP_CHAR* str, QSP_BOOL isRefresh) } /* Restarting the game */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPRestartGame(JNIEnv *env, jobject this, jboolean isRefresh) +JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_restartGame(JNIEnv *env, jobject this, jboolean isRefresh) { if (qspIsExitOnError && qspErrorNum) return JNI_FALSE; qspPrepareExecution(); diff --git a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java index f06433b..470d1b2 100644 --- a/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java +++ b/libcharlie/src/main/java/org/libsnxqsp/jni/SNXLib.java @@ -16,9 +16,8 @@ public record ErrorData(String locName, int errorNum, int index, int line) { } public native void init(); public native void terminate(); - public native boolean QSPIsInCallBack(); public native void QSPEnableDebugMode(boolean isDebug); - public native Object QSPGetCurStateData();//!!!STUB + public native ExecutionState QSPGetCurStateData(); public native String QSPGetVersion(); public native String QSPGetCompiledDateTime(); public native int QSPGetFullRefreshCount(); @@ -32,13 +31,12 @@ public record ErrorData(String locName, int errorNum, int index, int line) { } public native String getVarsDesc(); public native boolean isVarsDescChanged(); public native int getVarValuesCount(String name); - public native Object QSPGetVarValues(String name, int ind);//!!!STUB + public native VarValResp getVarValues(String name, int ind);//!!!STUB public native int QSPGetMaxVarsCount(); public native Object QSPGetVarNameByIndex(int index);//!!!STUB /* Input string */ - public native void QSPSetInputStrText(String val); + public native void setInputStrText(String val); /* Actions */ - public native int getActionsCount(); public native ListItem[] getActions(); public native boolean executeSelActionCode(boolean isRefresh); public native boolean setSelActionIndex(int ind, boolean isRefresh); @@ -46,7 +44,6 @@ public record ErrorData(String locName, int errorNum, int index, int line) { } public native boolean isActionsChanged(); /* Objects */ public native ListItem[] getObjects(); - public native int getObjectsCount(); public native boolean setSelObjectIndex(int ind, boolean isRefresh); public native int getSelObjectIndex(); public native boolean isObjectsChanged(); @@ -59,13 +56,10 @@ public record ErrorData(String locName, int errorNum, int index, int line) { } public native ErrorData getLastErrorData(); public native String getErrorDesc(int errorNum); /* Game */ - public native boolean QSPLoadGameWorld(String fileName); - public native boolean loadGameWorldFromData(byte[] data , int dataSize, String fileName); - public native boolean QSPSaveGame(String fileName, boolean isRefresh); - public native byte[] QSPSaveGameAsData(boolean isRefresh); - public native boolean QSPOpenSavedGame(String fileName, boolean isRefresh); - public native boolean QSPOpenSavedGameFromData(byte[] data , int dataSize, boolean isRefresh); - public native boolean QSPRestartGame(boolean isRefresh); + public native boolean loadGameWorldFromData(byte[] data, String fileName); + public native byte[] saveGameAsData(boolean isRefresh); + public native boolean openSavedGameFromData(byte[] data, boolean isRefresh); + public native boolean restartGame(boolean isRefresh); public void onSystem(String code) { } public void onDebug(String str) { } From f1d0ebcec6294530590e1aced1a85c425ad5b523 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 9 Mar 2025 05:22:20 +0300 Subject: [PATCH 34/57] Use the Kotlin contracts --- .../lib/impl/LibAlphaProxyImpl.kt | 14 +++-- .../lib/impl/LibBravoProxyImpl.kt | 54 +++++++++++-------- .../lib/impl/LibCharlieProxyImpl.kt | 40 ++++++++------ .../android/questopiabundle/utils/FileUtil.kt | 10 ++++ .../questopiabundle/utils/StringUtil.kt | 10 ++-- 5 files changed, 85 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 9358745..3d213bf 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -31,6 +31,7 @@ import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.Volatile +import kotlin.contracts.ExperimentalContracts class LibAlphaProxyImpl( private val context: Context, @@ -120,6 +121,7 @@ class LibAlphaProxyImpl( } } + @OptIn(ExperimentalContracts::class) private val actionsList: List get() { if (!isWritableDir(context, currGameDir)) return emptyList() @@ -134,7 +136,7 @@ class LibAlphaProxyImpl( val tempPath = normalizeContentPath(getFilename(tempImagePath)) val fileFromPath = gameDir?.child(context, tempPath) if (isWritableFile(context, fileFromPath)) { - tempImagePath = fileFromPath?.uri.toString() + tempImagePath = fileFromPath.uri.toString() } } @@ -144,6 +146,7 @@ class LibAlphaProxyImpl( return actions } + @OptIn(ExperimentalContracts::class) private val objectsList: List get() { if (!isWritableDir(context, currGameDir)) return emptyList() @@ -158,13 +161,13 @@ class LibAlphaProxyImpl( if (!isContainsHtmlTags(tempText)) { val fileFromPath = gameDir?.child(context, tempText) if (isWritableFile(context, fileFromPath)) { - tempImagePath = fileFromPath?.uri.toString() + tempImagePath = fileFromPath.uri.toString() } } else { val tempPath = getSrcDir(tempText) val fileFromPath = gameDir?.child(context, tempPath) if (isWritableFile(context, fileFromPath)) { - tempImagePath = fileFromPath?.uri.toString() + tempImagePath = fileFromPath.uri.toString() } } } @@ -370,6 +373,7 @@ class LibAlphaProxyImpl( ) } + @OptIn(ExperimentalContracts::class) override fun onShowImage(file: String) { if (!isNotEmptyOrBlank(file)) return gameInterface.showLibDialog(LibTypeDialog.DIALOG_PICTURE, file) @@ -383,15 +387,18 @@ class LibAlphaProxyImpl( gameInterface.showLibDialog(LibTypeDialog.DIALOG_MESSAGE, text) } + @OptIn(ExperimentalContracts::class) override fun onPlayFile(file: String, volume: Int) { if (!isNotEmptyOrBlank(file)) return gameInterface.playFile(file, volume) } + @OptIn(ExperimentalContracts::class) override fun onIsPlayingFile(file: String): Boolean { return isNotEmptyOrBlank(file) && gameInterface.isPlayingFile(file) } + @OptIn(ExperimentalContracts::class) override fun onCloseFile(file: String) { if (isNotEmptyOrBlank(file)) { gameInterface.closeFile(file) @@ -400,6 +407,7 @@ class LibAlphaProxyImpl( } } + @OptIn(ExperimentalContracts::class) override fun onOpenGameStatus(file: String?) { if (file == null) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index a4584ed..216c242 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -29,11 +29,11 @@ import org.qp.android.questopiabundle.utils.HtmlUtil.isContainsHtmlTags import org.qp.android.questopiabundle.utils.PathUtil.getFilename import org.qp.android.questopiabundle.utils.PathUtil.normalizeContentPath import org.qp.android.questopiabundle.utils.StringUtil.getStringOrEmpty -import org.qp.android.questopiabundle.utils.StringUtil.isEmptyOrBlank import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.Volatile +import kotlin.contracts.ExperimentalContracts class LibBravoProxyImpl( private val context: Context, @@ -166,58 +166,61 @@ class LibBravoProxyImpl( return changed } + @OptIn(ExperimentalContracts::class) private val actionsList: List get() { - if (!isWritableDir(context, currGameDir)) return emptyList() - val actions = mutableListOf() val gameDir = currGameDir + if (!isWritableDir(context, gameDir)) return emptyList() + + val actions = mutableListOf() for (element in QSPGetActionData() ?: return emptyList()) { val safeElement = element ?: continue - var tempImagePath = safeElement.image ?: "" - val tempText = safeElement.text ?: "" + var tempImagePath = safeElement.image + val tempText = safeElement.text if (isNotEmptyOrBlank(tempImagePath)) { val tempPath = normalizeContentPath(getFilename(tempImagePath)) - val fileFromPath = gameDir?.child(context, tempPath) + val fileFromPath = gameDir.child(context, tempPath) if (isWritableFile(context, fileFromPath)) { - tempImagePath = fileFromPath?.uri.toString() + tempImagePath = fileFromPath.uri.toString() } } - actions.add(LibListItem(tempText, tempImagePath)) + actions.add(LibListItem(tempText ?: "", tempImagePath ?: "")) } return actions } + @OptIn(ExperimentalContracts::class) private val objectsList: List get() { - if (!isWritableDir(context, currGameDir)) return emptyList() - val objects = mutableListOf() val gameDir = currGameDir + if (!isWritableDir(context, gameDir)) return emptyList() + val objects = mutableListOf() for (element in QSPGetObjectData() ?: return emptyList()) { val safeElement = element ?: continue - var tempImagePath = element.image ?: "" - val tempText = element.text ?: "" + var tempImagePath = safeElement.image + val tempText = safeElement.text if (tempText.contains(" get() { if (!isWritableDir(context, currGameDir)) return emptyList() @@ -175,7 +176,7 @@ class LibCharlieProxyImpl( val tempPath = normalizeContentPath(getFilename(tempImagePath)) val fileFromPath = gameDir?.child(context, tempPath) if (isWritableFile(context, fileFromPath)) { - tempImagePath = fileFromPath?.uri.toString() + tempImagePath = fileFromPath.uri.toString() } } @@ -185,27 +186,28 @@ class LibCharlieProxyImpl( return actions } + @OptIn(ExperimentalContracts::class) private val objectsList: List get() { - if (!isWritableDir(context, currGameDir)) return emptyList() - val objects = mutableListOf() val gameDir = currGameDir + if (!isWritableDir(context, gameDir)) return emptyList() + val objects = mutableListOf() for (element in getObjects()) { var tempImagePath = element.image ?: "" val tempText = element.text ?: "" if (tempText.contains(" Date: Mon, 10 Mar 2025 22:46:12 +0300 Subject: [PATCH 35/57] Roll back the file update --- .../main/java/org/libndkqsp/jni/NDKLib.java | 91 +++++++++++++ .../src/main/java/org/libndkqsp/jni/NDKLib.kt | 121 ------------------ 2 files changed, 91 insertions(+), 121 deletions(-) create mode 100644 libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java delete mode 100644 libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt diff --git a/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java new file mode 100644 index 0000000..b5b5eb9 --- /dev/null +++ b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java @@ -0,0 +1,91 @@ +package org.libndkqsp.jni; + +public abstract class NDKLib { + + public record ListItem(String image, String text) { } + + public record ExecutionState(String loc, int actIndex, int lineNum) { } + + public record VarValResp(boolean isSuccess , String stringValue , int intValue) { } + + public record ErrorData(String locName , int errorNum , int index , int line) { } + + static { + System.loadLibrary("ndkqsp"); + } + + public native void QSPInit(); + public native void QSPDeInit(); + public native boolean QSPIsInCallBack(); + public native void QSPEnableDebugMode(boolean isDebug); + public native ExecutionState QSPGetCurStateData();//!!!STUB + public native String QSPGetVersion(); + public native String QSPGetCompiledDateTime(); + public native int QSPGetFullRefreshCount(); + public native String QSPGetQstFullPath(); + public native String QSPGetCurLoc(); + public native Object QSPGetExprValue();//!!!STUB + /* Main desc */ + public native String QSPGetMainDesc(); + public native boolean QSPIsMainDescChanged(); + /* Vars desc */ + public native String QSPGetVarsDesc(); + public native boolean QSPIsVarsDescChanged(); + public native Object QSPGetVarValuesCount(String name); + public native Object QSPGetVarValues(String name, int ind);//!!!STUB + public native int QSPGetMaxVarsCount(); + public native Object QSPGetVarNameByIndex(int index);//!!!STUB + /* Input string */ + public native void QSPSetInputStrText(String val); + /* Actions */ + public native int QSPGetActionsCount(); + public native ListItem[] QSPGetActionData(); + public native boolean QSPExecuteSelActionCode(boolean isRefresh); + public native boolean QSPSetSelActionIndex(int ind, boolean isRefresh); + public native int QSPGetSelActionIndex(); + public native boolean QSPIsActionsChanged(); + /* Objects */ + public native ListItem[] QSPGetObjectData(); + public native int QSPGetObjectsCount(); + public native boolean QSPSetSelObjectIndex(int ind, boolean isRefresh); + public native int QSPGetSelObjectIndex(); + public native boolean QSPIsObjectsChanged(); + /* Code execution */ + public native boolean QSPExecString(String s, boolean isRefresh); + public native boolean QSPExecLocationCode(String name, boolean isRefresh); + public native boolean QSPExecCounter(boolean isRefresh); + public native boolean QSPExecUserInput(boolean isRefresh); + /* Errors */ + public native ErrorData QSPGetLastErrorData(); + public native String QSPGetErrorDesc(int errorNum); + /* Game */ + public native boolean QSPLoadGameWorld(String fileName); + public native boolean QSPLoadGameWorldFromData(byte[] data, String fileName); + public native boolean QSPSaveGame(String fileName, boolean isRefresh); + public native byte[] QSPSaveGameAsData(boolean isRefresh); + public native boolean QSPOpenSavedGame(String fileName, boolean isRefresh); + public native boolean QSPOpenSavedGameFromData(byte[] data , int dataSize, boolean isRefresh); + public native boolean QSPRestartGame(boolean isRefresh); + public native void QSPSelectMenuItem(int index); + //public native void QSPSetCallBack(int type, QSP_CALLBACK func) + + public void CallDebug(String str) {} + public void RefreshInt() { } + public void ShowPicture(String path) { } + public void SetTimer(int msecs) { } + public void ShowMessage(String message) { } + public void PlayFile(String path, int volume) { } + public boolean IsPlayingFile(final String path) { return false; } + public void CloseFile(String path) { } + public void OpenGame(String filename) { } + public void SaveGame(String filename) { } + public String InputBox(String prompt) { return null; } + public int GetMSCount() { return 0; } + public void AddMenuItem(String name, String imgPath) { } + public void ShowMenu() { } + public void DeleteMenu() { } + public void Wait(int msecs) { } + public void ShowWindow(int type, boolean isShow) { } + public byte[] GetFileContents(String path) { return null; } + public void ChangeQuestPath(String path) { } +} diff --git a/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt deleted file mode 100644 index 7aa73fd..0000000 --- a/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.kt +++ /dev/null @@ -1,121 +0,0 @@ -package org.libndkqsp.jni - -abstract class NDKLib { - @JvmRecord - data class ListItem(val image: String = "", val text: String = "") - - @JvmRecord - data class ExecutionState(val loc: String, val actIndex: Int, val lineNum: Int) - - @JvmRecord - data class VarValResp(val isSuccess: Boolean, val stringValue: String, val intValue: Int) - - @JvmRecord - data class ErrorData(val locName: String, val errorNum: Int, val index: Int, val line: Int) - - external fun QSPInit() - external fun QSPDeInit() - external fun QSPIsInCallBack(): Boolean - external fun QSPEnableDebugMode(isDebug: Boolean) - external fun QSPGetCurStateData(): Any? //!!!STUB - external fun QSPGetVersion(): String? - external fun QSPGetCompiledDateTime(): String? - external fun QSPGetFullRefreshCount(): Int - external fun QSPGetQstFullPath(): String? - external fun QSPGetCurLoc(): String? - external fun QSPGetExprValue(): Any? //!!!STUB - - /* Main desc */ - external fun QSPGetMainDesc(): String? - external fun QSPIsMainDescChanged(): Boolean - - /* Vars desc */ - external fun QSPGetVarsDesc(): String? - external fun QSPIsVarsDescChanged(): Boolean - external fun QSPGetVarValuesCount(name: String?): Any? - external fun QSPGetVarValues(name: String?, ind: Int): Any? //!!!STUB - external fun QSPGetMaxVarsCount(): Int - external fun QSPGetVarNameByIndex(index: Int): Any? //!!!STUB - - /* Input string */ - external fun QSPSetInputStrText(`val`: String?) - - /* Actions */ - external fun QSPGetActionsCount(): Int - external fun QSPGetActionData(): List? - external fun QSPExecuteSelActionCode(isRefresh: Boolean): Boolean - external fun QSPSetSelActionIndex(ind: Int, isRefresh: Boolean): Boolean - external fun QSPGetSelActionIndex(): Int - external fun QSPIsActionsChanged(): Boolean - - /* Objects */ - external fun QSPGetObjectData(): List? - external fun QSPGetObjectsCount(): Int - external fun QSPSetSelObjectIndex(ind: Int, isRefresh: Boolean): Boolean - external fun QSPGetSelObjectIndex(): Int - external fun QSPIsObjectsChanged(): Boolean - - /* Code execution */ - external fun QSPExecString(s: String?, isRefresh: Boolean): Boolean - external fun QSPExecLocationCode(name: String?, isRefresh: Boolean): Boolean - external fun QSPExecCounter(isRefresh: Boolean): Boolean - external fun QSPExecUserInput(isRefresh: Boolean): Boolean - - /* Errors */ - external fun QSPGetLastErrorData(): ErrorData? - external fun QSPGetErrorDesc(errorNum: Int): String? - - /* Game */ - external fun QSPLoadGameWorld(fileName: String?): Boolean - external fun QSPLoadGameWorldFromData(data: ByteArray?, fileName: String?): Boolean - external fun QSPSaveGame(fileName: String?, isRefresh: Boolean): Boolean - external fun QSPSaveGameAsData(isRefresh: Boolean): ByteArray? - external fun QSPOpenSavedGame(fileName: String?, isRefresh: Boolean): Boolean - external fun QSPOpenSavedGameFromData( - data: ByteArray?, - dataSize: Int, - isRefresh: Boolean - ): Boolean - - external fun QSPRestartGame(isRefresh: Boolean): Boolean - external fun QSPSelectMenuItem(index: Int) - - //public native void QSPSetCallBack(int type, QSP_CALLBACK func) - fun CallDebug(str: String?) {} - open fun RefreshInt() {} - open fun ShowPicture(path: String?) {} - open fun SetTimer(msecs: Int) {} - open fun ShowMessage(message: String?) {} - open fun PlayFile(path: String?, volume: Int) {} - open fun IsPlayingFile(path: String?): Boolean { - return false - } - - open fun CloseFile(path: String?) {} - open fun OpenGame(filename: String?) {} - open fun SaveGame(filename: String?) {} - open fun InputBox(prompt: String?): String? { - return null - } - - open fun GetMSCount(): Int { - return 0 - } - - open fun AddMenuItem(name: String?, imgPath: String?) {} - open fun ShowMenu() {} - open fun DeleteMenu() {} - open fun Wait(msecs: Int) {} - open fun ShowWindow(type: Int, isShow: Boolean) {} - open fun GetFileContents(path: String?): ByteArray? { - return null - } - - open fun ChangeQuestPath(path: String?) {} - - companion object { - init { - System.loadLibrary("ndkqsp") - } - } -} From 866b2528a34c4700ce1ea43c38c794b16ec104ca Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:18:35 +0300 Subject: [PATCH 36/57] Refactor `*ProxyImpl` * Brought onRefreshInt and loadInterfaceConfiguration methods in accordance with the meaning of data class --- .../lib/impl/LibAlphaProxyImpl.kt | 18 ++-- .../lib/impl/LibBravoProxyImpl.kt | 82 ++++++------------- .../lib/impl/LibCharlieProxyImpl.kt | 82 ++++++------------- 3 files changed, 61 insertions(+), 121 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 3d213bf..76546dc 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -353,17 +353,21 @@ class LibAlphaProxyImpl( objectsList = objectsList ) } else { - gameState.copy( - mainDesc = if (isMainDescChanged && gameState.mainDesc != mainDesc) mainDesc else gameState.mainDesc, - varsDesc = if (isVarsDescChanged && gameState.varsDesc != varsDesc) varsDesc else gameState.varsDesc, - actionsList = if (isActsChanged && gameState.actionsList !== actionsList) actionsList else gameState.actionsList, - objectsList = if (isObjsChanged && gameState.objectsList !== objectsList) objectsList else gameState.objectsList + val newState = gameState.copy( + mainDesc = if (isMainDescChanged) mainDesc else gameState.mainDesc, + varsDesc = if (isVarsDescChanged) varsDesc else gameState.varsDesc, + actionsList = if (isActsChanged) actionsList else gameState.actionsList, + objectsList = if (isObjsChanged) objectsList else gameState.objectsList ) + + when (newState != gameState) { + true -> newState + false -> gameState + } } - val request = LibRefIRequest() gameInterface.doRefresh( - request.copy( + LibRefIRequest( isIConfigChanged = loadInterfaceConfiguration(), isMainDescChanged = isMainDescChanged, isVarsDescChanged = isVarsDescChanged, diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 216c242..5dffc7e 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -107,63 +107,30 @@ class LibBravoProxyImpl( * @return `true` if the configuration has changed, otherwise `false` */ private fun loadInterfaceConfiguration(): Boolean { - val config = gameState.interfaceConfig - var changed = false + val oldConfig = gameState.interfaceConfig val htmlResult = QSPGetVarValues("USEHTML", 0) as VarValResp - if (htmlResult.isSuccess) { - val useHtml = htmlResult.intValue != 0 - if (config.useHtml != useHtml) { - gameState = gameState.copy( - interfaceConfig = config.copy( - useHtml = useHtml - ) - ) - changed = true - } - } - val fSizeResult = QSPGetVarValues("FSIZE", 0) as VarValResp - if (fSizeResult.isSuccess && config.fontSize != fSizeResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - fontSize = fSizeResult.intValue.toLong() - ) - ) - changed = true - } - val bColorResult = QSPGetVarValues("BCOLOR", 0) as VarValResp - if (bColorResult.isSuccess && config.backColor != bColorResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - backColor = bColorResult.intValue.toLong() - ) - ) - changed = true - } - val fColorResult = QSPGetVarValues("FCOLOR", 0) as VarValResp - if (fColorResult.isSuccess && config.fontColor != fColorResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - fontColor = fColorResult.intValue.toLong() - ) - ) - changed = true - } - val lColorResult = QSPGetVarValues("LCOLOR", 0) as VarValResp - if (lColorResult.isSuccess && config.linkColor != lColorResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - linkColor = lColorResult.intValue.toLong() - ) - ) - changed = true - } - return changed + val useHtml = htmlResult.intValue != 0 + val newConfig = oldConfig.copy( + useHtml = if (htmlResult.isSuccess) useHtml else oldConfig.useHtml, + fontSize = if (fSizeResult.isSuccess) fSizeResult.intValue.toLong() else oldConfig.fontSize, + backColor = if (bColorResult.isSuccess) bColorResult.intValue.toLong() else oldConfig.backColor, + fontColor = if (fColorResult.isSuccess) fColorResult.intValue.toLong() else oldConfig.fontColor, + linkColor = if (lColorResult.isSuccess) lColorResult.intValue.toLong() else oldConfig.linkColor + ) + + return when { + newConfig != oldConfig -> { + gameState = gameState.copy(interfaceConfig = newConfig) + true + } + else -> false + } } @OptIn(ExperimentalContracts::class) @@ -396,16 +363,17 @@ class LibBravoProxyImpl( // endregion LibQpProxy // region LibQpCallbacks override fun RefreshInt() { - gameState = gameState.copy( - mainDesc = if (QSPIsMainDescChanged() && gameState.mainDesc != QSPGetMainDesc()) QSPGetMainDesc() ?: "" else gameState.mainDesc, - varsDesc = if (QSPIsVarsDescChanged() && gameState.varsDesc != QSPGetVarsDesc()) QSPGetVarsDesc() ?: "" else gameState.varsDesc, - actionsList = if (QSPIsActionsChanged() && gameState.actionsList !== actionsList) actionsList else gameState.actionsList, - objectsList = if (QSPIsObjectsChanged() && gameState.objectsList !== objectsList) objectsList else gameState.objectsList + val newState = gameState.copy( + mainDesc = if (QSPIsMainDescChanged()) QSPGetMainDesc() ?: "" else gameState.mainDesc, + varsDesc = if (QSPIsVarsDescChanged()) QSPGetVarsDesc() ?: "" else gameState.varsDesc, + actionsList = if (QSPIsActionsChanged()) actionsList else gameState.actionsList, + objectsList = if (QSPIsObjectsChanged()) objectsList else gameState.objectsList ) - val request = LibRefIRequest() + gameState = if (newState != gameState) newState else gameState + gameInterface.doRefresh( - request.copy( + LibRefIRequest( isIConfigChanged = loadInterfaceConfiguration(), isMainDescChanged = QSPIsMainDescChanged(), isVarsDescChanged = QSPIsVarsDescChanged(), diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index b52d8c0..a0e47f4 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -102,63 +102,30 @@ class LibCharlieProxyImpl( * @return `true` if the configuration has changed, otherwise `false` */ private fun loadInterfaceConfiguration(): Boolean { - val config = gameState.interfaceConfig - var changed = false + val oldConfig = gameState.interfaceConfig val htmlResult = getVarValues("USEHTML", 0) - if (htmlResult.isSuccess) { - val useHtml = htmlResult.intValue != 0 - if (config.useHtml != useHtml) { - gameState = gameState.copy( - interfaceConfig = config.copy( - useHtml = useHtml - ) - ) - changed = true - } - } - val fSizeResult = getVarValues("FSIZE", 0) - if (fSizeResult.isSuccess && config.fontSize != fSizeResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - fontSize = fSizeResult.intValue.toLong() - ) - ) - changed = true - } - val bColorResult = getVarValues("BCOLOR", 0) - if (bColorResult.isSuccess && config.backColor != bColorResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - backColor = bColorResult.intValue.toLong() - ) - ) - changed = true - } - val fColorResult = getVarValues("FCOLOR", 0) - if (fColorResult.isSuccess && config.fontColor != fColorResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - fontColor = fColorResult.intValue.toLong() - ) - ) - changed = true - } - val lColorResult = getVarValues("LCOLOR", 0) - if (lColorResult.isSuccess && config.linkColor != lColorResult.intValue.toLong()) { - gameState = gameState.copy( - interfaceConfig = config.copy( - linkColor = lColorResult.intValue.toLong() - ) - ) - changed = true - } - return changed + val useHtml = htmlResult.intValue != 0 + val newConfig = oldConfig.copy( + useHtml = if (htmlResult.isSuccess) useHtml else oldConfig.useHtml, + fontSize = if (fSizeResult.isSuccess) fSizeResult.intValue.toLong() else oldConfig.fontSize, + backColor = if (bColorResult.isSuccess) bColorResult.intValue.toLong() else oldConfig.backColor, + fontColor = if (fColorResult.isSuccess) fColorResult.intValue.toLong() else oldConfig.fontColor, + linkColor = if (lColorResult.isSuccess) lColorResult.intValue.toLong() else oldConfig.linkColor + ) + + return when { + newConfig != oldConfig -> { + gameState = gameState.copy(interfaceConfig = newConfig) + true + } + else -> false + } } @OptIn(ExperimentalContracts::class) @@ -386,16 +353,17 @@ class LibCharlieProxyImpl( } override fun onRefreshInt() { - gameState = gameState.copy( - mainDesc = if (isMainDescChanged && gameState.mainDesc != mainDesc) mainDesc ?: "" else gameState.mainDesc, - varsDesc = if (isVarsDescChanged && gameState.varsDesc != varsDesc) varsDesc ?: "" else gameState.varsDesc, - actionsList = if (isActionsChanged && gameState.actionsList !== actionsList) actionsList else gameState.actionsList, - objectsList = if (isObjectsChanged && gameState.objectsList !== objectsList) objectsList else gameState.objectsList + val newState = gameState.copy( + mainDesc = if (isMainDescChanged) mainDesc else gameState.mainDesc, + varsDesc = if (isVarsDescChanged) varsDesc else gameState.varsDesc, + actionsList = if (isActionsChanged) actionsList else gameState.actionsList, + objectsList = if (isObjectsChanged) objectsList else gameState.objectsList ) - val request = LibRefIRequest() + gameState = if (newState != gameState) newState else gameState + gameInterface.doRefresh( - request.copy( + LibRefIRequest( isIConfigChanged = loadInterfaceConfiguration(), isMainDescChanged = isMainDescChanged, isVarsDescChanged = isVarsDescChanged, From 1189e45d6bcea5e3059b0230cf832ef9aa077c86 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:35:32 +0300 Subject: [PATCH 37/57] Add a nullability check --- .../android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index a0e47f4..e24e26c 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -70,8 +70,8 @@ class LibCharlieProxyImpl( private fun loadGameWorld(): Boolean { val gameFileUri = gameState.gameFileUri - val gameFile = fromUri(context, gameState.gameFileUri) - val gameFileFullPath = documentWrap(gameFile!!).getAbsolutePath(context) + val gameFile = fromUri(context, gameState.gameFileUri) ?: return false + val gameFileFullPath = documentWrap(gameFile).getAbsolutePath(context) val gameData = getFileContents(context, gameFileUri) ?: return false if (!loadGameWorldFromData(gameData, gameFileFullPath)) { From ccbcfd735b44dc0c126d7a1bdd0cd35315fca0f4 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:37:30 +0300 Subject: [PATCH 38/57] Remove the use of the debugger --- .../android/questopiabundle/QuestopiaBundle.kt | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index fe9b4d5..f31c141 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -256,23 +256,6 @@ class QuestopiaBundle : Service(), GameInterface { gameDirUri: Uri, gameFileUri: Uri ) { - Log.i( - javaClass.simpleName, - checkCallingPermission("android.permission.WRITE_EXTERNAL_STORAGE").toString() - ) - Log.i( - javaClass.simpleName, - checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE").toString() - ) - Log.i( - javaClass.simpleName, getAccessibleAbsolutePaths( - baseContext - ).toString() - ) - Log.d( - javaClass.simpleName, - "Debug: \nGameID|$gameId\nGameTitle|$gameTitle\nGameDirUri|$gameDirUri\nGameFileUri|$gameFileUri" - ) when (mLibVersion) { 570 -> libBravoProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri) From 7a4920ebb5dbf0fc800e0b71279d9154f25c1da2 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:38:31 +0300 Subject: [PATCH 39/57] Get rid of lateinit and the setCallback() method --- .../questopiabundle/QuestopiaBundle.kt | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index f31c141..085ed0d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -23,9 +23,9 @@ class QuestopiaBundle : Service(), GameInterface { private val counterHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) private val counterNDKHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) private val counterSNXHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) - private lateinit var libAlphaProxy: LibAlphaProxyImpl - private lateinit var libBravoProxy: LibBravoProxyImpl - private lateinit var libCharlieProxy: LibCharlieProxyImpl + private val libAlphaProxy: LibAlphaProxyImpl = LibAlphaProxyImpl(this@QuestopiaBundle) + private val libBravoProxy: LibBravoProxyImpl = LibBravoProxyImpl(this@QuestopiaBundle) + private val libCharlieProxy: LibCharlieProxyImpl = LibCharlieProxyImpl(this@QuestopiaBundle) private var callbacks: AsyncCallbacks? = null @Volatile @@ -102,14 +102,6 @@ class QuestopiaBundle : Service(), GameInterface { } } - fun setCallback() { - when (mLibVersion) { - 570 -> counterNDKHandler.postDelayed(counterNDKTask, counterInterval.toLong()) - 575 -> counterSNXHandler.postDelayed(counterSNXTask, counterInterval.toLong()) - 592 -> counterHandler.postDelayed(counterTask, counterInterval.toLong()) - } - } - private fun removeCallback() { counterHandler.removeCallbacks(counterTask) counterNDKHandler.removeCallbacks(counterNDKTask) @@ -211,24 +203,21 @@ class QuestopiaBundle : Service(), GameInterface { mLibVersion = libVer when (mLibVersion) { 570 -> { - libBravoProxy = LibBravoProxyImpl(this@QuestopiaBundle) - setCallback() + counterNDKHandler.postDelayed(counterNDKTask, counterInterval.toLong()) libBravoProxy.setGameInterface(this@QuestopiaBundle) libBravoProxy.startLibThread() } 575 -> { - libCharlieProxy = LibCharlieProxyImpl(this@QuestopiaBundle) - setCallback() + counterSNXHandler.postDelayed(counterSNXTask, counterInterval.toLong()) libCharlieProxy.setGameInterface(this@QuestopiaBundle) libCharlieProxy.startLibThread() } 592 -> { - libAlphaProxy = LibAlphaProxyImpl(this@QuestopiaBundle) - setCallback() + counterHandler.postDelayed(counterTask, counterInterval.toLong()) libAlphaProxy.setGameInterface(this@QuestopiaBundle) libAlphaProxy.startLibThread() From 690540f82dc55363b6bbc86c9c63e47376708f88 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:39:57 +0300 Subject: [PATCH 40/57] Make the data class immutable --- .../qp/android/questopiabundle/dto/LibListItem.kt | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt index 8454efd..03c9a1c 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt @@ -2,21 +2,12 @@ package org.qp.android.questopiabundle.dto import android.os.Parcel import android.os.Parcelable -import com.libqsp.jni.QSPLib -import org.libndkqsp.jni.NDKLib -import org.libsnxqsp.jni.SNXLib data class LibListItem( - var text: String = "", - var pathToImage: String = "" + val text: String = "", + val pathToImage: String = "" ) : Parcelable { - constructor(item: QSPLib.ListItem) : this(item.name, item.image ?: "") - - constructor(item: NDKLib.ListItem) : this(item.text, item.image ?: "") - - constructor(item: SNXLib.ListItem) : this(item.text, item.image ?: "") - constructor(source: Parcel) : this( source.readString() ?: "", source.readString() ?: "" From 44a430fad90ed972b1480e4b70118a140e3766c7 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 16 May 2025 19:58:38 +0300 Subject: [PATCH 41/57] Use delayed initialization for the callbacks field --- .../questopiabundle/QuestopiaBundle.kt | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index 085ed0d..62247b3 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -8,7 +8,6 @@ import android.os.IBinder import android.os.Looper import android.os.RemoteException import android.util.Log -import com.anggrayudi.storage.file.DocumentFileCompat.getAccessibleAbsolutePaths import org.qp.android.questopiabundle.lib.LibGameRequest import org.qp.android.questopiabundle.lib.LibGameState import org.qp.android.questopiabundle.lib.LibRefIRequest @@ -26,7 +25,7 @@ class QuestopiaBundle : Service(), GameInterface { private val libAlphaProxy: LibAlphaProxyImpl = LibAlphaProxyImpl(this@QuestopiaBundle) private val libBravoProxy: LibBravoProxyImpl = LibBravoProxyImpl(this@QuestopiaBundle) private val libCharlieProxy: LibCharlieProxyImpl = LibCharlieProxyImpl(this@QuestopiaBundle) - private var callbacks: AsyncCallbacks? = null + private lateinit var callbacks: AsyncCallbacks @Volatile private var counterInterval = 500 @@ -54,7 +53,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun requestPermFile(pathUri: Uri) { try { - callbacks?.requestPermOnFile(pathUri) + callbacks.requestPermOnFile(pathUri) } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) } @@ -62,7 +61,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun requestCreateFile(dirUri: Uri, path: String): Uri { try { - return callbacks?.requestCreateFile(dirUri, path) ?: Uri.EMPTY + return callbacks.requestCreateFile(dirUri, path) ?: Uri.EMPTY } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) return Uri.EMPTY @@ -71,7 +70,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun isPlayingFile(filePath: String): Boolean { try { - return callbacks?.isPlayingFile(filePath) ?: false + return callbacks.isPlayingFile(filePath) } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) return false @@ -80,7 +79,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun closeAllFiles() { try { - callbacks?.closeAllFiles() + callbacks.closeAllFiles() } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) } @@ -88,7 +87,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun closeFile(filePath: String?) { try { - callbacks?.closeFile(filePath) + callbacks.closeFile(filePath) } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) } @@ -96,7 +95,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun playFile(path: String?, volume: Int) { try { - callbacks?.playFile(path, volume) + callbacks.playFile(path, volume) } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) } @@ -110,7 +109,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun doChangeCurrGameDir(newGameDirUri: Uri?) { try { - callbacks?.sendChangeCurrGameDir(newGameDirUri) + callbacks.sendChangeCurrGameDir(newGameDirUri) } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) } @@ -141,7 +140,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun showLibDialog(dialog: LibTypeDialog?, inputString: String?): LibDialogRetValue? { try { - return callbacks?.doOnShowDialog(LibResult(dialog), inputString) + return callbacks.doOnShowDialog(LibResult(dialog), inputString) } catch (e: RemoteException) { Log.e("QuestopiaBundle", "Error", e) return LibDialogRetValue() @@ -150,7 +149,7 @@ class QuestopiaBundle : Service(), GameInterface { override fun changeVisWindow(type: LibTypeWindow?, show: Boolean) { try { - callbacks?.doChangeVisWindow(LibResult(type), show) + callbacks.doChangeVisWindow(LibResult(type), show) } catch (e: RemoteException) { Log.e("QuestopiaBundle", "Error", e) } @@ -328,7 +327,7 @@ class QuestopiaBundle : Service(), GameInterface { @Throws(RemoteException::class) override fun sendAsync(callbacks: AsyncCallbacks?) { - this@QuestopiaBundle.callbacks = callbacks + this@QuestopiaBundle.callbacks = callbacks ?: return } } From 50d2ea6c13863b7ecf4bb24b8dcbb50b072854f1 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 16 May 2025 20:02:17 +0300 Subject: [PATCH 42/57] Combine methods for sending the current state * Reduce the number of updates sent --- .../questopiabundle/AsyncCallbacks.aidl | 3 +-- .../android/questopiabundle/GameInterface.kt | 2 +- .../questopiabundle/QuestopiaBundle.kt | 11 ++++------ .../lib/impl/LibAlphaProxyImpl.kt | 20 +++++++++++------- .../lib/impl/LibBravoProxyImpl.kt | 21 ++++++++++++------- .../lib/impl/LibCharlieProxyImpl.kt | 20 +++++++++++------- 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl b/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl index 0af82eb..1973187 100644 --- a/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl +++ b/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl @@ -6,8 +6,7 @@ import org.qp.android.questopiabundle.LibException; import org.qp.android.questopiabundle.LibDialogRetValue; interface AsyncCallbacks { - void sendLibGameState(in LibResult libResult); - void sendLibRef(in LibResult libResult); + void updateState(in LibResult refReq, in LibResult newState); void sendChangeCurrGameDir(in Uri gameDirUri); LibDialogRetValue doOnShowDialog(in LibResult typeDialog, String inputString); diff --git a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt index 81e5ca3..71dd4d5 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt @@ -13,7 +13,7 @@ interface GameInterface { fun closeFile(filePath: String?) fun playFile(path: String?, volume: Int) fun doChangeCurrGameDir(newGameDirUri: Uri?) - fun doRefresh(request: LibRefIRequest?) + fun doUpdateState(request: LibRefIRequest) fun showLibDialog(dialog: LibTypeDialog?, inputString: String?): LibDialogRetValue? fun changeVisWindow(type: LibTypeWindow?, show: Boolean) /** diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index 62247b3..99588ba 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -115,22 +115,19 @@ class QuestopiaBundle : Service(), GameInterface { } } - override fun doRefresh(request: LibRefIRequest?) { + override fun doUpdateState(request: LibRefIRequest) { try { when (mLibVersion) { 570 -> { - callbacks?.sendLibRef(LibResult(request)) - callbacks?.sendLibGameState(LibResult(libBravoProxy.gameState)) + callbacks.updateState(LibResult(request), LibResult(libBravoProxy.gameState)) } 575 -> { - callbacks?.sendLibRef(LibResult(request)) - callbacks?.sendLibGameState(LibResult(libCharlieProxy.gameState)) + callbacks.updateState(LibResult(request), LibResult(libCharlieProxy.gameState)) } 592 -> { - callbacks?.sendLibRef(LibResult(request)) - callbacks?.sendLibGameState(LibResult(libAlphaProxy.gameState)) + callbacks.updateState(LibResult(request), LibResult(libAlphaProxy.gameState)) } } } catch (e: Exception) { diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 76546dc..9e4e9fb 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -35,6 +35,7 @@ import kotlin.contracts.ExperimentalContracts class LibAlphaProxyImpl( private val context: Context, + private var gameRequest: LibRefIRequest = LibRefIRequest(), override var gameState: LibGameState = LibGameState() ) : QSPLib(), LibIProxy { @@ -366,15 +367,18 @@ class LibAlphaProxyImpl( } } - gameInterface.doRefresh( - LibRefIRequest( - isIConfigChanged = loadInterfaceConfiguration(), - isMainDescChanged = isMainDescChanged, - isVarsDescChanged = isVarsDescChanged, - isActionsChanged = isActsChanged, - isObjectsChanged = isObjsChanged - ) + val newRequest = gameRequest.copy( + isIConfigChanged = loadInterfaceConfiguration(), + isMainDescChanged = isMainDescChanged, + isVarsDescChanged = isVarsDescChanged, + isActionsChanged = isActsChanged, + isObjectsChanged = isObjsChanged ) + + if (newRequest != gameRequest) { + gameRequest = newRequest + gameInterface.doUpdateState(newRequest) + } } @OptIn(ExperimentalContracts::class) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 5dffc7e..d5e333d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -37,6 +37,7 @@ import kotlin.contracts.ExperimentalContracts class LibBravoProxyImpl( private val context: Context, + private var gameRequest: LibRefIRequest = LibRefIRequest(), override var gameState: LibGameState = LibGameState() ) : NDKLib(), LibIProxy { @@ -129,6 +130,7 @@ class LibBravoProxyImpl( gameState = gameState.copy(interfaceConfig = newConfig) true } + else -> false } } @@ -372,15 +374,18 @@ class LibBravoProxyImpl( gameState = if (newState != gameState) newState else gameState - gameInterface.doRefresh( - LibRefIRequest( - isIConfigChanged = loadInterfaceConfiguration(), - isMainDescChanged = QSPIsMainDescChanged(), - isVarsDescChanged = QSPIsVarsDescChanged(), - isActionsChanged = QSPIsActionsChanged(), - isObjectsChanged = QSPIsObjectsChanged() - ) + val newRequest = gameRequest.copy( + isIConfigChanged = loadInterfaceConfiguration(), + isMainDescChanged = QSPIsMainDescChanged(), + isVarsDescChanged = QSPIsVarsDescChanged(), + isActionsChanged = QSPIsActionsChanged(), + isObjectsChanged = QSPIsObjectsChanged() ) + + if (newRequest != gameRequest) { + gameRequest = newRequest + gameInterface.doUpdateState(newRequest) + } } @OptIn(ExperimentalContracts::class) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index e24e26c..0005863 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -37,6 +37,7 @@ import kotlin.contracts.ExperimentalContracts class LibCharlieProxyImpl( private val context: Context, + private var gameRequest: LibRefIRequest = LibRefIRequest(), override var gameState: LibGameState = LibGameState() ) : SNXLib(), LibIProxy { @@ -362,15 +363,18 @@ class LibCharlieProxyImpl( gameState = if (newState != gameState) newState else gameState - gameInterface.doRefresh( - LibRefIRequest( - isIConfigChanged = loadInterfaceConfiguration(), - isMainDescChanged = isMainDescChanged, - isVarsDescChanged = isVarsDescChanged, - isActionsChanged = isActionsChanged, - isObjectsChanged = isObjectsChanged - ) + val newRequest = gameRequest.copy( + isIConfigChanged = loadInterfaceConfiguration(), + isMainDescChanged = isMainDescChanged, + isVarsDescChanged = isVarsDescChanged, + isActionsChanged = isActionsChanged, + isObjectsChanged = isObjectsChanged ) + + if (newRequest != gameRequest) { + gameRequest = newRequest + gameInterface.doUpdateState(newRequest) + } } @OptIn(ExperimentalContracts::class) From 27e810d13aa21bc4addeae7770e5d7b8b280d40a Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 16 May 2025 20:45:52 +0300 Subject: [PATCH 43/57] Combine LibListItem and LibListItem into one class * Move LibIConfig and LibGameState to the dto package --- .../questopiabundle/QuestopiaBundle.kt | 2 +- .../{lib => dto}/LibGameState.kt | 19 +++++------ .../dto/{LibListItem.kt => LibGenItem.kt} | 16 +++++----- .../{lib => dto}/LibIConfig.kt | 2 +- .../questopiabundle/dto/LibMenuItem.kt | 32 ------------------- .../android/questopiabundle/lib/LibIProxy.kt | 1 + .../lib/impl/LibAlphaProxyImpl.kt | 16 +++++----- .../lib/impl/LibBravoProxyImpl.kt | 19 ++++++----- .../lib/impl/LibCharlieProxyImpl.kt | 19 ++++++----- 9 files changed, 45 insertions(+), 81 deletions(-) rename app/src/main/java/org/qp/android/questopiabundle/{lib => dto}/LibGameState.kt (76%) rename app/src/main/java/org/qp/android/questopiabundle/dto/{LibListItem.kt => LibGenItem.kt} (58%) rename app/src/main/java/org/qp/android/questopiabundle/{lib => dto}/LibIConfig.kt (95%) delete mode 100644 app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index 99588ba..90ad4c5 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -9,7 +9,7 @@ import android.os.Looper import android.os.RemoteException import android.util.Log import org.qp.android.questopiabundle.lib.LibGameRequest -import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog import org.qp.android.questopiabundle.lib.LibTypeWindow diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGameState.kt similarity index 76% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt rename to app/src/main/java/org/qp/android/questopiabundle/dto/LibGameState.kt index 31ec137..e368e17 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGameState.kt @@ -1,11 +1,8 @@ -package org.qp.android.questopiabundle.lib +package org.qp.android.questopiabundle.dto import android.net.Uri import android.os.Parcel import android.os.Parcelable -import org.qp.android.questopiabundle.dto.LibListItem -import org.qp.android.questopiabundle.dto.LibMenuItem -import org.qp.android.questopiabundle.lib.LibIConfig data class LibGameState( val interfaceConfig: LibIConfig = LibIConfig(), @@ -16,9 +13,9 @@ data class LibGameState( val gameFileUri: Uri = Uri.EMPTY, val mainDesc: String = "", val varsDesc: String = "", - val actionsList: List = listOf(), - val objectsList: List = listOf(), - val menuItemsList: List = listOf() + val actionsList: List = listOf(), + val objectsList: List = listOf(), + val menuItemsList: List = listOf() ) : Parcelable { constructor(source: Parcel) : this( @@ -31,9 +28,9 @@ data class LibGameState( source.readString() ?: "", source.readString() ?: "", ) { - source.readTypedList(actionsList, LibListItem) - source.readTypedList(objectsList, LibListItem) - source.readTypedList(menuItemsList, LibMenuItem) + source.readTypedList(actionsList, LibGenItem) + source.readTypedList(objectsList, LibGenItem) + source.readTypedList(menuItemsList, LibGenItem) } override fun writeToParcel(dest: Parcel, flags: Int) { @@ -61,4 +58,4 @@ data class LibGameState( return arrayOfNulls(size) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGenItem.kt similarity index 58% rename from app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt rename to app/src/main/java/org/qp/android/questopiabundle/dto/LibGenItem.kt index 03c9a1c..4cbb3dd 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGenItem.kt @@ -3,9 +3,9 @@ package org.qp.android.questopiabundle.dto import android.os.Parcel import android.os.Parcelable -data class LibListItem( +data class LibGenItem( val text: String = "", - val pathToImage: String = "" + val imagePath: String = "" ) : Parcelable { constructor(source: Parcel) : this( @@ -17,16 +17,16 @@ data class LibListItem( override fun writeToParcel(dest: Parcel, flags: Int) { dest.writeString(text) - dest.writeString(pathToImage) + dest.writeString(imagePath) } - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(source: Parcel): LibListItem { - return LibListItem(source) + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibGenItem { + return LibGenItem(source) } - override fun newArray(size: Int): Array { + override fun newArray(size: Int): Array { return arrayOfNulls(size) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibIConfig.kt similarity index 95% rename from app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt rename to app/src/main/java/org/qp/android/questopiabundle/dto/LibIConfig.kt index 960d1ee..2c42d33 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibIConfig.kt @@ -1,4 +1,4 @@ -package org.qp.android.questopiabundle.lib +package org.qp.android.questopiabundle.dto import android.os.Parcel import android.os.Parcelable diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt deleted file mode 100644 index 141e0a9..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.qp.android.questopiabundle.dto - -import android.os.Parcel -import android.os.Parcelable - -data class LibMenuItem( - val name: String = "", - val pathToImage: String = "" -) : Parcelable { - - constructor(source: Parcel) : this( - source.readString() ?: "", - source.readString() ?: "" - ) - - override fun describeContents(): Int = 0 - - override fun writeToParcel(dest: Parcel, flags: Int) { - dest.writeString(name) - dest.writeString(pathToImage) - } - - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(source: Parcel): LibMenuItem { - return LibMenuItem(source) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } -} diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt index 573988d..0e576d1 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt @@ -2,6 +2,7 @@ package org.qp.android.questopiabundle.lib import android.net.Uri import org.qp.android.questopiabundle.GameInterface +import org.qp.android.questopiabundle.dto.LibGameState interface LibIProxy { /** diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 9e4e9fb..7331053 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -11,8 +11,8 @@ import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import com.libqsp.jni.QSPLib import org.qp.android.questopiabundle.GameInterface -import org.qp.android.questopiabundle.dto.LibListItem -import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.dto.LibGenItem +import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.lib.LibIProxy import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog @@ -123,10 +123,10 @@ class LibAlphaProxyImpl( } @OptIn(ExperimentalContracts::class) - private val actionsList: List + private val actionsList: List get() { if (!isWritableDir(context, currGameDir)) return emptyList() - val actions = mutableListOf() + val actions = mutableListOf() val gameDir = currGameDir for (element in getActions()) { @@ -141,17 +141,17 @@ class LibAlphaProxyImpl( } } - actions.add(LibListItem(tempText, tempImagePath)) + actions.add(LibGenItem(tempText, tempImagePath)) } return actions } @OptIn(ExperimentalContracts::class) - private val objectsList: List + private val objectsList: List get() { if (!isWritableDir(context, currGameDir)) return emptyList() - val objects = mutableListOf() + val objects = mutableListOf() val gameDir = currGameDir for (element in getObjects()) { @@ -173,7 +173,7 @@ class LibAlphaProxyImpl( } } - objects.add(LibListItem(tempText, tempImagePath)) + objects.add(LibGenItem(tempText, tempImagePath)) } return objects diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index d5e333d..61e6b49 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -11,9 +11,8 @@ import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import org.libndkqsp.jni.NDKLib import org.qp.android.questopiabundle.GameInterface -import org.qp.android.questopiabundle.dto.LibListItem -import org.qp.android.questopiabundle.dto.LibMenuItem -import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.dto.LibGenItem +import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.lib.LibIProxy import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog @@ -136,12 +135,12 @@ class LibBravoProxyImpl( } @OptIn(ExperimentalContracts::class) - private val actionsList: List + private val actionsList: List get() { val gameDir = currGameDir if (!isWritableDir(context, gameDir)) return emptyList() - val actions = mutableListOf() + val actions = mutableListOf() for (element in QSPGetActionData() ?: return emptyList()) { val safeElement = element ?: continue @@ -156,19 +155,19 @@ class LibBravoProxyImpl( } } - actions.add(LibListItem(tempText ?: "", tempImagePath ?: "")) + actions.add(LibGenItem(tempText ?: "", tempImagePath ?: "")) } return actions } @OptIn(ExperimentalContracts::class) - private val objectsList: List + private val objectsList: List get() { val gameDir = currGameDir if (!isWritableDir(context, gameDir)) return emptyList() - val objects = mutableListOf() + val objects = mutableListOf() for (element in QSPGetObjectData() ?: return emptyList()) { val safeElement = element ?: continue var tempImagePath = safeElement.image @@ -189,7 +188,7 @@ class LibBravoProxyImpl( } } - objects.add(LibListItem(tempText ?: "", tempImagePath ?: "")) + objects.add(LibGenItem(tempText ?: "", tempImagePath ?: "")) } return objects @@ -472,7 +471,7 @@ class LibBravoProxyImpl( } override fun AddMenuItem(name: String?, imgPath: String?) { - gameState = gameState.copy(menuItemsList = listOf(LibMenuItem( + gameState = gameState.copy(menuItemsList = listOf(LibGenItem( name ?: "", imgPath ?: "" ))) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index 0005863..e4ef847 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -11,9 +11,8 @@ import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import org.libsnxqsp.jni.SNXLib import org.qp.android.questopiabundle.GameInterface -import org.qp.android.questopiabundle.dto.LibListItem -import org.qp.android.questopiabundle.dto.LibMenuItem -import org.qp.android.questopiabundle.lib.LibGameState +import org.qp.android.questopiabundle.dto.LibGenItem +import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.lib.LibIProxy import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog @@ -130,10 +129,10 @@ class LibCharlieProxyImpl( } @OptIn(ExperimentalContracts::class) - private val actionsList: List + private val actionsList: List get() { if (!isWritableDir(context, currGameDir)) return emptyList() - val actions = mutableListOf() + val actions = mutableListOf() val gameDir = currGameDir for (element in getActions()) { @@ -148,19 +147,19 @@ class LibCharlieProxyImpl( } } - actions.add(LibListItem(tempText, tempImagePath)) + actions.add(LibGenItem(tempText, tempImagePath)) } return actions } @OptIn(ExperimentalContracts::class) - private val objectsList: List + private val objectsList: List get() { val gameDir = currGameDir if (!isWritableDir(context, gameDir)) return emptyList() - val objects = mutableListOf() + val objects = mutableListOf() for (element in getObjects()) { var tempImagePath = element.image ?: "" val tempText = element.text ?: "" @@ -180,7 +179,7 @@ class LibCharlieProxyImpl( } } - objects.add(LibListItem(tempText, tempImagePath)) + objects.add(LibGenItem(tempText, tempImagePath)) } return objects @@ -466,7 +465,7 @@ class LibCharlieProxyImpl( } override fun onAddMenuItem(name: String?, imgPath: String?) { - gameState = gameState.copy(menuItemsList = listOf(LibMenuItem( + gameState = gameState.copy(menuItemsList = listOf(LibGenItem( name ?: "", imgPath ?: "" ))) From 22c4b4e702b3201108b1f2106a43ed060da4bdcd Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 16 May 2025 21:25:58 +0300 Subject: [PATCH 44/57] Replace the setter with a variable with initialization in the constructor * Use HandlerCompat to initialize an asynchronous Handler --- .../questopiabundle/QuestopiaBundle.kt | 22 +++++++------------ .../android/questopiabundle/lib/LibIProxy.kt | 2 +- .../lib/impl/LibAlphaProxyImpl.kt | 13 +++++------ .../lib/impl/LibBravoProxyImpl.kt | 13 +++++------ .../lib/impl/LibCharlieProxyImpl.kt | 13 +++++------ 5 files changed, 24 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index 90ad4c5..524eaa2 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -3,13 +3,13 @@ package org.qp.android.questopiabundle import android.app.Service import android.content.Intent import android.net.Uri -import android.os.Handler import android.os.IBinder import android.os.Looper import android.os.RemoteException import android.util.Log -import org.qp.android.questopiabundle.lib.LibGameRequest +import androidx.core.os.HandlerCompat import org.qp.android.questopiabundle.dto.LibGameState +import org.qp.android.questopiabundle.lib.LibGameRequest import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog import org.qp.android.questopiabundle.lib.LibTypeWindow @@ -19,12 +19,12 @@ import org.qp.android.questopiabundle.lib.impl.LibCharlieProxyImpl import kotlin.concurrent.Volatile class QuestopiaBundle : Service(), GameInterface { - private val counterHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) - private val counterNDKHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) - private val counterSNXHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) - private val libAlphaProxy: LibAlphaProxyImpl = LibAlphaProxyImpl(this@QuestopiaBundle) - private val libBravoProxy: LibBravoProxyImpl = LibBravoProxyImpl(this@QuestopiaBundle) - private val libCharlieProxy: LibCharlieProxyImpl = LibCharlieProxyImpl(this@QuestopiaBundle) + private val counterHandler = HandlerCompat.createAsync(Looper.myLooper() ?: Looper.getMainLooper()) + private val counterNDKHandler = HandlerCompat.createAsync(Looper.myLooper() ?: Looper.getMainLooper()) + private val counterSNXHandler = HandlerCompat.createAsync(Looper.myLooper() ?: Looper.getMainLooper()) + private val libAlphaProxy: LibAlphaProxyImpl = LibAlphaProxyImpl(this@QuestopiaBundle, this) + private val libBravoProxy: LibBravoProxyImpl = LibBravoProxyImpl(this@QuestopiaBundle, this) + private val libCharlieProxy: LibCharlieProxyImpl = LibCharlieProxyImpl(this@QuestopiaBundle, this) private lateinit var callbacks: AsyncCallbacks @Volatile @@ -200,22 +200,16 @@ class QuestopiaBundle : Service(), GameInterface { when (mLibVersion) { 570 -> { counterNDKHandler.postDelayed(counterNDKTask, counterInterval.toLong()) - - libBravoProxy.setGameInterface(this@QuestopiaBundle) libBravoProxy.startLibThread() } 575 -> { counterSNXHandler.postDelayed(counterSNXTask, counterInterval.toLong()) - - libCharlieProxy.setGameInterface(this@QuestopiaBundle) libCharlieProxy.startLibThread() } 592 -> { counterHandler.postDelayed(counterTask, counterInterval.toLong()) - - libAlphaProxy.setGameInterface(this@QuestopiaBundle) libAlphaProxy.startLibThread() } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt index 0e576d1..e1ff611 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt @@ -31,5 +31,5 @@ interface LibIProxy { */ fun executeCounter() val gameState: LibGameState - fun setGameInterface(inter: GameInterface) + val gameInterface: GameInterface } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 7331053..23fe3a6 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -6,6 +6,7 @@ import android.os.Handler import android.os.Looper import android.os.SystemClock import android.util.Log +import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child @@ -35,8 +36,9 @@ import kotlin.contracts.ExperimentalContracts class LibAlphaProxyImpl( private val context: Context, - private var gameRequest: LibRefIRequest = LibRefIRequest(), - override var gameState: LibGameState = LibGameState() + override var gameInterface: GameInterface, + override var gameState: LibGameState = LibGameState(), + private var gameRequest: LibRefIRequest = LibRefIRequest() ) : QSPLib(), LibIProxy { private val TAG = javaClass.simpleName @@ -46,7 +48,6 @@ class LibAlphaProxyImpl( @Volatile private var libThreadInit = false @Volatile private var gameStartTime: Long = 0L @Volatile private var lastMsCountCallTime: Long = 0L - private lateinit var gameInterface: GameInterface private val currGameDir: DocumentFile? get() = fromUri(context, gameState.gameDirUri) @@ -187,7 +188,7 @@ class LibAlphaProxyImpl( if (Looper.myLooper() == null) { Looper.prepare() } - libHandler = Handler(Looper.myLooper()!!) + libHandler = HandlerCompat.createAsync(Looper.myLooper()!!) libThreadInit = true Looper.loop() terminate() @@ -341,10 +342,6 @@ class LibAlphaProxyImpl( } } - override fun setGameInterface(inter: GameInterface) { - this.gameInterface = inter - } - override fun onRefreshInt(isForced: Boolean) { gameState = if (isForced) { gameState.copy( diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 61e6b49..a07685c 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -6,6 +6,7 @@ import android.os.Handler import android.os.Looper import android.os.SystemClock import android.util.Log +import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child @@ -36,8 +37,9 @@ import kotlin.contracts.ExperimentalContracts class LibBravoProxyImpl( private val context: Context, - private var gameRequest: LibRefIRequest = LibRefIRequest(), - override var gameState: LibGameState = LibGameState() + override var gameInterface: GameInterface, + override var gameState: LibGameState = LibGameState(), + private var gameRequest: LibRefIRequest = LibRefIRequest() ) : NDKLib(), LibIProxy { private val TAG = javaClass.simpleName @@ -47,7 +49,6 @@ class LibBravoProxyImpl( @Volatile private var libThreadInit = false @Volatile private var gameStartTime: Long = 0L @Volatile private var lastMsCountCallTime: Long = 0L - private lateinit var gameInterface: GameInterface private val currGameDir: DocumentFile? get() = fromUri(context, gameState.gameDirUri) @@ -203,7 +204,7 @@ class LibBravoProxyImpl( if (Looper.myLooper() == null) { Looper.prepare() } - libHandler = Handler(Looper.myLooper()!!) + libHandler = HandlerCompat.createAsync(Looper.myLooper()!!) libThreadInit = true Looper.loop() QSPDeInit() @@ -357,10 +358,6 @@ class LibBravoProxyImpl( } } - override fun setGameInterface(inter: GameInterface) { - this.gameInterface = inter - } - // endregion LibQpProxy // region LibQpCallbacks override fun RefreshInt() { diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index e4ef847..fe5c5b6 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -6,6 +6,7 @@ import android.os.Handler import android.os.Looper import android.os.SystemClock import android.util.Log +import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child @@ -36,8 +37,9 @@ import kotlin.contracts.ExperimentalContracts class LibCharlieProxyImpl( private val context: Context, - private var gameRequest: LibRefIRequest = LibRefIRequest(), - override var gameState: LibGameState = LibGameState() + override var gameInterface: GameInterface, + override var gameState: LibGameState = LibGameState(), + private var gameRequest: LibRefIRequest = LibRefIRequest() ) : SNXLib(), LibIProxy { private val TAG = javaClass.simpleName @@ -47,7 +49,6 @@ class LibCharlieProxyImpl( @Volatile private var libThreadInit = false @Volatile private var gameStartTime: Long = 0L @Volatile private var lastMsCountCallTime: Long = 0L - private lateinit var gameInterface: GameInterface private val currGameDir: DocumentFile? get() = fromUri(context, gameState.gameDirUri) @@ -194,7 +195,7 @@ class LibCharlieProxyImpl( if (Looper.myLooper() == null) { Looper.prepare() } - libHandler = Handler(Looper.myLooper()!!) + libHandler = HandlerCompat.createAsync(Looper.myLooper()!!) libThreadInit = true Looper.loop() terminate() @@ -348,10 +349,6 @@ class LibCharlieProxyImpl( } } - override fun setGameInterface(inter: GameInterface) { - this.gameInterface = inter - } - override fun onRefreshInt() { val newState = gameState.copy( mainDesc = if (isMainDescChanged) mainDesc else gameState.mainDesc, From 8afd89233335d21bad3c4817cbccea3d7e7c1a45 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 17 May 2025 02:26:47 +0300 Subject: [PATCH 45/57] Remove the useless operator --- .../main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index 524eaa2..e9f425f 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -225,7 +225,6 @@ class QuestopiaBundle : Service(), GameInterface { 592 -> libAlphaProxy.stopLibThread() } - stopSelf() } @Throws(RemoteException::class) From cae7712f193a007aaaebce25c16f2feca7146093 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 17 May 2025 02:27:59 +0300 Subject: [PATCH 46/57] Use an inline function instead of try-finally --- .../questopiabundle/lib/impl/LibAlphaProxyImpl.kt | 14 ++++---------- .../questopiabundle/lib/impl/LibBravoProxyImpl.kt | 12 +++--------- .../lib/impl/LibCharlieProxyImpl.kt | 14 ++++---------- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 23fe3a6..8c8d74d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -12,8 +12,8 @@ import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import com.libqsp.jni.QSPLib import org.qp.android.questopiabundle.GameInterface -import org.qp.android.questopiabundle.dto.LibGenItem import org.qp.android.questopiabundle.dto.LibGameState +import org.qp.android.questopiabundle.dto.LibGenItem import org.qp.android.questopiabundle.lib.LibIProxy import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog @@ -32,6 +32,7 @@ import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.Volatile +import kotlin.concurrent.withLock import kotlin.contracts.ExperimentalContracts class LibAlphaProxyImpl( @@ -51,20 +52,13 @@ class LibAlphaProxyImpl( private val currGameDir: DocumentFile? get() = fromUri(context, gameState.gameDirUri) - @Synchronized private fun runOnQspThread(runnable: Runnable) { if (!libThreadInit) { Log.w(TAG, "Lib thread has been started, but not initialized!") return } - val mLibHandler = libHandler - mLibHandler.post { - libLock.lock() - try { - runnable.run() - } finally { - libLock.unlock() - } + libHandler.post { + libLock.withLock { runnable.run() } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index a07685c..914fd77 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -33,6 +33,7 @@ import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.Volatile +import kotlin.concurrent.withLock import kotlin.contracts.ExperimentalContracts class LibBravoProxyImpl( @@ -52,20 +53,13 @@ class LibBravoProxyImpl( private val currGameDir: DocumentFile? get() = fromUri(context, gameState.gameDirUri) - @Synchronized private fun runOnQspThread(runnable: Runnable) { if (!libThreadInit) { Log.w(TAG, "Lib thread has been started, but not initialized!") return } - val mLibHandler = libHandler - mLibHandler.post { - libLock.lock() - try { - runnable.run() - } finally { - libLock.unlock() - } + libHandler.post { + libLock.withLock { runnable.run() } } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index fe5c5b6..a946f04 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -12,8 +12,8 @@ import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import org.libsnxqsp.jni.SNXLib import org.qp.android.questopiabundle.GameInterface -import org.qp.android.questopiabundle.dto.LibGenItem import org.qp.android.questopiabundle.dto.LibGameState +import org.qp.android.questopiabundle.dto.LibGenItem import org.qp.android.questopiabundle.lib.LibIProxy import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog @@ -33,6 +33,7 @@ import org.qp.android.questopiabundle.utils.StringUtil.isNotEmptyOrBlank import org.qp.android.questopiabundle.utils.ThreadUtil.isSameThread import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.Volatile +import kotlin.concurrent.withLock import kotlin.contracts.ExperimentalContracts class LibCharlieProxyImpl( @@ -52,20 +53,13 @@ class LibCharlieProxyImpl( private val currGameDir: DocumentFile? get() = fromUri(context, gameState.gameDirUri) - @Synchronized private fun runOnQspThread(runnable: Runnable) { if (!libThreadInit) { Log.w(TAG, "Lib thread has been started, but not initialized!") return } - val mLibHandler = libHandler - mLibHandler.post { - libLock.lock() - try { - runnable.run() - } finally { - libLock.unlock() - } + libHandler.post { + libLock.withLock { runnable.run() } } } From a093e1a9766fb713b923f7c457b6ed4e7135fa88 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 23 May 2025 00:49:48 +0300 Subject: [PATCH 47/57] Remove useless synchronization for the library thread stop method --- .../org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt | 1 - .../org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt | 1 - .../qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt | 1 - 3 files changed, 3 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 8c8d74d..d11962d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -195,7 +195,6 @@ class LibAlphaProxyImpl( libThread.start() } - @Synchronized override fun stopLibThread() { if (libThreadInit) { val handler = libHandler diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 914fd77..a12d70a 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -211,7 +211,6 @@ class LibBravoProxyImpl( libThread.start() } - @Synchronized override fun stopLibThread() { if (libThreadInit) { val handler = libHandler diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index a946f04..22a85ce 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -202,7 +202,6 @@ class LibCharlieProxyImpl( libThread.start() } - @Synchronized override fun stopLibThread() { if (libThreadInit) { val handler = libHandler From 76802e4af43848b48f5a189ef7048f33de4f6883 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 23 May 2025 00:50:54 +0300 Subject: [PATCH 48/57] Add a function call to remove handlers --- .../main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index e9f425f..88865fa 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -225,6 +225,7 @@ class QuestopiaBundle : Service(), GameInterface { 592 -> libAlphaProxy.stopLibThread() } + removeCallback() } @Throws(RemoteException::class) From 23824a6916d7bf62358dc6ff09b9980a4d022b75 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Fri, 23 May 2025 01:23:37 +0300 Subject: [PATCH 49/57] Remove meaningless use of the logger --- .../lib/impl/LibAlphaProxyImpl.kt | 21 ++++++------------ .../lib/impl/LibBravoProxyImpl.kt | 22 ++++++------------- .../lib/impl/LibCharlieProxyImpl.kt | 20 +++++------------ 3 files changed, 20 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index d11962d..8e53097 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -53,12 +53,10 @@ class LibAlphaProxyImpl( get() = fromUri(context, gameState.gameDirUri) private fun runOnQspThread(runnable: Runnable) { - if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!") - return - } - libHandler.post { - libLock.withLock { runnable.run() } + if (libThreadInit) { + libHandler.post { + libLock.withLock { runnable.run() } + } } } @@ -187,7 +185,6 @@ class LibAlphaProxyImpl( Looper.loop() terminate() } catch (t: Throwable) { - Log.e(TAG, "lib thread has stopped exceptionally", t) Thread.currentThread().interrupt() } } @@ -200,9 +197,8 @@ class LibAlphaProxyImpl( val handler = libHandler handler.looper.quitSafely() libThreadInit = false - } else { - Log.w(TAG, "lib thread has been started, but not initialized") } + libThread.interrupt() } @@ -417,11 +413,9 @@ class LibAlphaProxyImpl( gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } } else { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found") - Log.e(TAG, "Save file not found") } } catch (e: Exception) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()) - Log.e(TAG, "Error: ", e) } } } @@ -436,7 +430,6 @@ class LibAlphaProxyImpl( saveGameState(saveFileUri) } else { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir") - Log.e(TAG, "Error access dir") } } } @@ -470,7 +463,7 @@ class LibAlphaProxyImpl( try { Thread.sleep(msecs.toLong()) } catch (ex: InterruptedException) { - Log.e(TAG, "Wait failed", ex) + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, ex.toString()) } } @@ -482,7 +475,7 @@ class LibAlphaProxyImpl( override fun onOpenGame(file: String, isNewGame: Boolean) { val newGameDir = fromFullPath(context, file) if (newGameDir == null || !newGameDir.exists()) { - Log.e(TAG, "Game directory not found: $file") + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Game directory not found: $file") return } val currGameDir = currGameDir ?: return diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index a12d70a..b901b90 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -43,7 +43,6 @@ class LibBravoProxyImpl( private var gameRequest: LibRefIRequest = LibRefIRequest() ) : NDKLib(), LibIProxy { - private val TAG = javaClass.simpleName private val libLock = ReentrantLock() private lateinit var libThread: Thread @Volatile private lateinit var libHandler: Handler @@ -54,12 +53,10 @@ class LibBravoProxyImpl( get() = fromUri(context, gameState.gameDirUri) private fun runOnQspThread(runnable: Runnable) { - if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!") - return - } - libHandler.post { - libLock.withLock { runnable.run() } + if (libThreadInit) { + libHandler.post { + libLock.withLock { runnable.run() } + } } } @@ -75,10 +72,8 @@ class LibBravoProxyImpl( if (!QSPLoadGameWorldFromData(gameData, gameFileFullPath)) { showLastQspError() - Log.d("QSP", "World is not loaded!") return false } - Log.d("QSP", "World is loaded!") return true } @@ -203,7 +198,6 @@ class LibBravoProxyImpl( Looper.loop() QSPDeInit() } catch (t: Throwable) { - Log.e(TAG, "lib thread has stopped exceptionally", t) Thread.currentThread().interrupt() } } @@ -216,9 +210,8 @@ class LibBravoProxyImpl( val handler = libHandler handler.looper.quitSafely() libThreadInit = false - } else { - Log.w(TAG, "lib thread has been started, but not initialized") } + libThread.interrupt() } @@ -440,7 +433,6 @@ class LibBravoProxyImpl( saveGameState(saveFileUri) } else { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir") - Log.e(TAG, "Error access dir") } } } @@ -483,7 +475,7 @@ class LibBravoProxyImpl( try { Thread.sleep(msecs.toLong()) } catch (ex: InterruptedException) { - Log.e(TAG, "Wait failed", ex) + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, ex.toString()) } } @@ -509,7 +501,7 @@ class LibBravoProxyImpl( val newGameDir = fromFullPath(context, path.toString()) if (newGameDir == null || !newGameDir.exists()) { - Log.e(TAG, "Game directory not found: $path") + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Game directory not found: $path") return } val currGameDirUri = currGameDir?.uri diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index 22a85ce..ebb0a90 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -43,7 +43,6 @@ class LibCharlieProxyImpl( private var gameRequest: LibRefIRequest = LibRefIRequest() ) : SNXLib(), LibIProxy { - private val TAG = javaClass.simpleName private val libLock = ReentrantLock() private lateinit var libThread: Thread @Volatile private lateinit var libHandler: Handler @@ -54,12 +53,10 @@ class LibCharlieProxyImpl( get() = fromUri(context, gameState.gameDirUri) private fun runOnQspThread(runnable: Runnable) { - if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!") - return - } - libHandler.post { - libLock.withLock { runnable.run() } + if (libThreadInit) { + libHandler.post { + libLock.withLock { runnable.run() } + } } } @@ -194,7 +191,6 @@ class LibCharlieProxyImpl( Looper.loop() terminate() } catch (t: Throwable) { - Log.e(TAG, "lib thread has stopped exceptionally", t) Thread.currentThread().interrupt() } } @@ -207,9 +203,8 @@ class LibCharlieProxyImpl( val handler = libHandler handler.looper.quitSafely() libThreadInit = false - } else { - Log.w(TAG, "lib thread has been started, but not initialized") } + libThread.interrupt() } @@ -414,11 +409,9 @@ class LibCharlieProxyImpl( gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } } else { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found") - Log.e(TAG, "Save file not found") } } catch (e: Exception) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()) - Log.e(TAG, "Error: ", e) } } } @@ -434,7 +427,6 @@ class LibCharlieProxyImpl( saveGameState(saveFileUri) } else { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir") - Log.e(TAG, "Error access dir") } } } @@ -479,7 +471,7 @@ class LibCharlieProxyImpl( try { Thread.sleep(msecs.toLong()) } catch (ex: InterruptedException) { - Log.e(TAG, "Wait failed", ex) + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, ex.toString()) } } From 3782d4847095e03e76eb0017ff02b899e73f1749 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:02:27 +0300 Subject: [PATCH 50/57] Fix places that could potentially cause an NPE error --- .../lib/impl/LibAlphaProxyImpl.kt | 6 +++-- .../lib/impl/LibBravoProxyImpl.kt | 22 ++++++++----------- .../lib/impl/LibCharlieProxyImpl.kt | 2 ++ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 8e53097..ce66029 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -123,6 +123,7 @@ class LibAlphaProxyImpl( val gameDir = currGameDir for (element in getActions()) { + if (element == null) continue var tempImagePath = element.image val tempText = element.name @@ -148,8 +149,9 @@ class LibAlphaProxyImpl( val gameDir = currGameDir for (element in getObjects()) { - var tempImagePath = element.image - val tempText = element.name + if (element == null) continue + var tempImagePath = element.image ?: "" + val tempText = element.name ?: "" if (tempText.contains("() for (element in QSPGetActionData() ?: return emptyList()) { - val safeElement = element ?: continue - var tempImagePath = safeElement.image - val tempText = safeElement.text + if (element == null) continue + var tempImagePath = element.image ?: "" + val tempText = element.text ?: "" if (isNotEmptyOrBlank(tempImagePath)) { val tempPath = normalizeContentPath(getFilename(tempImagePath)) @@ -145,7 +141,7 @@ class LibBravoProxyImpl( } } - actions.add(LibGenItem(tempText ?: "", tempImagePath ?: "")) + actions.add(LibGenItem(tempText, tempImagePath)) } return actions @@ -159,9 +155,9 @@ class LibBravoProxyImpl( val objects = mutableListOf() for (element in QSPGetObjectData() ?: return emptyList()) { - val safeElement = element ?: continue - var tempImagePath = safeElement.image - val tempText = safeElement.text + if (element == null) continue + var tempImagePath = element.image ?: "" + val tempText = element.text ?: "" if (tempText.contains("() for (element in getObjects()) { + if (element == null) continue var tempImagePath = element.image ?: "" val tempText = element.text ?: "" From e8b7c1f7ee0b1eb11a562aece40a0fb60b7a3a02 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:04:52 +0300 Subject: [PATCH 51/57] Replace the method of requesting permission for a file with the method of obtaining a file --- .../questopiabundle/AsyncCallbacks.aidl | 2 +- .../android/questopiabundle/GameInterface.kt | 2 +- .../questopiabundle/QuestopiaBundle.kt | 5 +++-- .../lib/impl/LibAlphaProxyImpl.kt | 17 +++++++-------- .../lib/impl/LibBravoProxyImpl.kt | 21 ++++++++----------- .../lib/impl/LibCharlieProxyImpl.kt | 6 ++---- 6 files changed, 23 insertions(+), 30 deletions(-) diff --git a/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl b/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl index 1973187..a39099b 100644 --- a/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl +++ b/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl @@ -17,7 +17,7 @@ interface AsyncCallbacks { void closeFile(String filePath); void playFile(String path, int volume); - void requestPermOnFile(in Uri fileUri); + Uri requestReceiveFile(in String filePath); Uri requestCreateFile(in Uri fileUri, String path); void onError(in LibException libException); diff --git a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt index 71dd4d5..bd948cc 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt @@ -6,7 +6,7 @@ import org.qp.android.questopiabundle.lib.LibTypeDialog import org.qp.android.questopiabundle.lib.LibTypeWindow interface GameInterface { - fun requestPermFile(pathUri: Uri) + fun requestReceiveFile(filePath: String): Uri fun requestCreateFile(dirUri: Uri, path: String): Uri fun isPlayingFile(filePath: String): Boolean fun closeAllFiles() diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index 88865fa..c29664a 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -51,11 +51,12 @@ class QuestopiaBundle : Service(), GameInterface { @Volatile private var mLibVersion = 0 - override fun requestPermFile(pathUri: Uri) { + override fun requestReceiveFile(filePath: String): Uri { try { - callbacks.requestPermOnFile(pathUri) + return callbacks.requestReceiveFile(filePath) } catch (e: Exception) { Log.e(javaClass.simpleName, "Error", e) + return Uri.EMPTY } } diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index ce66029..511796e 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -8,6 +8,7 @@ import android.os.SystemClock import android.util.Log import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile +import com.anggrayudi.storage.extension.toDocumentFile import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import com.libqsp.jni.QSPLib @@ -42,7 +43,6 @@ class LibAlphaProxyImpl( private var gameRequest: LibRefIRequest = LibRefIRequest() ) : QSPLib(), LibIProxy { - private val TAG = javaClass.simpleName private val libLock = ReentrantLock() private lateinit var libThread: Thread @Volatile private lateinit var libHandler: Handler @@ -254,7 +254,6 @@ class LibAlphaProxyImpl( return } - gameInterface.requestPermFile(uri) val gameData = getFileContents(context, uri) ?: return if (!openSavedGameFromData(gameData, true)) { showLastQspError() @@ -267,7 +266,6 @@ class LibAlphaProxyImpl( return } - gameInterface.requestPermFile(uri) val gameData = saveGameAsData(false) ?: return writeFileContents(context, uri, gameData) } @@ -409,8 +407,7 @@ class LibAlphaProxyImpl( gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) } else { try { - val saveFile = fromFullPath(context, file) ?: return - gameInterface.requestPermFile(saveFile.uri) + val saveFile = gameInterface.requestReceiveFile(file).toDocumentFile(context) if (isWritableFile(context, saveFile)) { gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } } else { @@ -474,15 +471,15 @@ class LibAlphaProxyImpl( gameInterface.changeVisWindow(windowType, toShow) } + @OptIn(ExperimentalContracts::class) override fun onOpenGame(file: String, isNewGame: Boolean) { - val newGameDir = fromFullPath(context, file) - if (newGameDir == null || !newGameDir.exists()) { + if (!isNotEmptyOrBlank(file)) return + val currGameDirUri = currGameDir?.uri ?: return + val newGameDirUri = gameInterface.requestReceiveFile(file) + if (newGameDirUri == Uri.EMPTY) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Game directory not found: $file") return } - val currGameDir = currGameDir ?: return - val currGameDirUri = currGameDir.uri - val newGameDirUri = newGameDir.uri if (currGameDirUri != newGameDirUri) { gameState = gameState.copy(gameDirUri = newGameDirUri) gameInterface.doChangeCurrGameDir(newGameDirUri) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 2afd70c..8342861 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -8,10 +8,12 @@ import android.os.SystemClock import android.util.Log import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile +import com.anggrayudi.storage.extension.toDocumentFile import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import org.libndkqsp.jni.NDKLib import org.qp.android.questopiabundle.GameInterface +import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.dto.LibGenItem import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.lib.LibIProxy @@ -261,7 +263,6 @@ class LibBravoProxyImpl( return } - gameInterface.requestPermFile(uri) val gameData = getFileContents(context, uri) ?: return if (!QSPOpenSavedGameFromData(gameData, gameData.size, true)) { showLastQspError() @@ -275,7 +276,6 @@ class LibBravoProxyImpl( } val gameData = QSPSaveGameAsData(false) ?: return - gameInterface.requestPermFile(uri) writeFileContents(context, uri, gameData) } @@ -406,8 +406,7 @@ class LibBravoProxyImpl( gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) } else { try { - val saveFile = fromFullPath(context, filename) ?: return - gameInterface.requestPermFile(saveFile.uri) + val saveFile = gameInterface.requestReceiveFile(filename).toDocumentFile(context) if (isWritableFile(context, saveFile)) { gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } } else { @@ -484,9 +483,9 @@ class LibBravoProxyImpl( override fun GetFileContents(path: String?): ByteArray? { if (!isNotEmptyOrBlank(path)) return byteArrayOf() - val targetFile = fromFullPath(context, path.toString()) ?: return null - val targetFileUri = targetFile.uri - gameInterface.requestPermFile(targetFileUri) + val targetFile = gameInterface.requestReceiveFile(path).toDocumentFile(context) + val targetFileUri = targetFile?.uri ?: return null + if (targetFileUri == Uri.EMPTY) return null return getFileContents(context, targetFileUri) } @@ -494,14 +493,12 @@ class LibBravoProxyImpl( @OptIn(ExperimentalContracts::class) override fun ChangeQuestPath(path: String?) { if (!isNotEmptyOrBlank(path)) return - - val newGameDir = fromFullPath(context, path.toString()) - if (newGameDir == null || !newGameDir.exists()) { + val currGameDirUri = currGameDir?.uri ?: return + val newGameDirUri = gameInterface.requestReceiveFile(path) + if (newGameDirUri == Uri.EMPTY) { gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Game directory not found: $path") return } - val currGameDirUri = currGameDir?.uri - val newGameDirUri = newGameDir.uri if (currGameDirUri != newGameDirUri) { gameState = gameState.copy(gameDirUri = newGameDirUri) gameInterface.doChangeCurrGameDir(newGameDirUri) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index 45fbf98..1790612 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -8,6 +8,7 @@ import android.os.SystemClock import android.util.Log import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile +import com.anggrayudi.storage.extension.toDocumentFile import com.anggrayudi.storage.file.DocumentFileCompat.fromUri import com.anggrayudi.storage.file.child import org.libsnxqsp.jni.SNXLib @@ -260,7 +261,6 @@ class LibCharlieProxyImpl( return } - gameInterface.requestPermFile(uri) val gameData = getFileContents(context, uri) ?: return if (!openSavedGameFromData(gameData, true)) { showLastQspError() @@ -273,7 +273,6 @@ class LibCharlieProxyImpl( return } - gameInterface.requestPermFile(uri) val gameData = saveGameAsData(false) ?: return writeFileContents(context, uri, gameData) } @@ -405,8 +404,7 @@ class LibCharlieProxyImpl( gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) } else { try { - val saveFile = fromFullPath(context, filename) ?: return - gameInterface.requestPermFile(saveFile.uri) + val saveFile = gameInterface.requestReceiveFile(filename).toDocumentFile(context) if (isWritableFile(context, saveFile)) { gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } } else { From b260b7391b838ef5ca07ae905c002e7bd3880dc6 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:05:45 +0300 Subject: [PATCH 52/57] Remove unused methods and imports --- .../lib/impl/LibAlphaProxyImpl.kt | 2 -- .../lib/impl/LibBravoProxyImpl.kt | 3 --- .../lib/impl/LibCharlieProxyImpl.kt | 2 -- .../qp/android/questopiabundle/utils/FileUtil.kt | 16 ---------------- 4 files changed, 23 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt index 511796e..4c896e6 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.os.Handler import android.os.Looper import android.os.SystemClock -import android.util.Log import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.extension.toDocumentFile @@ -19,7 +18,6 @@ import org.qp.android.questopiabundle.lib.LibIProxy import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog import org.qp.android.questopiabundle.lib.LibTypeWindow -import org.qp.android.questopiabundle.utils.FileUtil.fromFullPath import org.qp.android.questopiabundle.utils.FileUtil.getFileContents import org.qp.android.questopiabundle.utils.FileUtil.isWritableDir import org.qp.android.questopiabundle.utils.FileUtil.isWritableFile diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt index 8342861..c57033d 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.os.Handler import android.os.Looper import android.os.SystemClock -import android.util.Log import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.extension.toDocumentFile @@ -15,13 +14,11 @@ import org.libndkqsp.jni.NDKLib import org.qp.android.questopiabundle.GameInterface import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.dto.LibGenItem -import org.qp.android.questopiabundle.dto.LibGameState import org.qp.android.questopiabundle.lib.LibIProxy import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog import org.qp.android.questopiabundle.lib.LibTypeWindow import org.qp.android.questopiabundle.utils.FileUtil.documentWrap -import org.qp.android.questopiabundle.utils.FileUtil.fromFullPath import org.qp.android.questopiabundle.utils.FileUtil.getFileContents import org.qp.android.questopiabundle.utils.FileUtil.isWritableDir import org.qp.android.questopiabundle.utils.FileUtil.isWritableFile diff --git a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt index 1790612..1208597 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.os.Handler import android.os.Looper import android.os.SystemClock -import android.util.Log import androidx.core.os.HandlerCompat import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.extension.toDocumentFile @@ -20,7 +19,6 @@ import org.qp.android.questopiabundle.lib.LibRefIRequest import org.qp.android.questopiabundle.lib.LibTypeDialog import org.qp.android.questopiabundle.lib.LibTypeWindow import org.qp.android.questopiabundle.utils.FileUtil.documentWrap -import org.qp.android.questopiabundle.utils.FileUtil.fromFullPath import org.qp.android.questopiabundle.utils.FileUtil.getFileContents import org.qp.android.questopiabundle.utils.FileUtil.isWritableDir import org.qp.android.questopiabundle.utils.FileUtil.isWritableFile diff --git a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt index 8de34b2..b4cb7b7 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/FileUtil.kt @@ -79,20 +79,4 @@ object FileUtil { Log.e("FileUtil", "Error reading file: $uriContent", ex) } } - - fun fromRelPath( - context: Context, - path: String, - parentDir: DocumentFile, - requiresWriteAccess: Boolean - ): DocumentFile? { - return parentDir.child(context, path, requiresWriteAccess) - } - - fun fromFullPath( - context: Context, - fullPath: String - ): DocumentFile? { - return DocumentFileCompat.fromFullPath(context, fullPath, requiresWriteAccess = true) - } } From f6b32d98c62b5fb61c551c988c14642b9b7d7794 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:06:44 +0300 Subject: [PATCH 53/57] Update project dependencies --- gradle/libs.versions.toml | 22 +++++++++++----------- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cb2339e..f331f14 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,28 +1,28 @@ [versions] -agp = "8.7.3" +agp = "8.10.1" kotlin = "2.0.21" -coreKtx = "1.15.0" -junit = "4.13.2" +coreKtx = "1.16.0" +junit = "5.13.0" junitVersion = "1.2.1" espressoCore = "3.6.1" -appcompat = "1.7.0" +appcompat = "1.7.1" material = "1.12.0" constraintlayout = "2.2.0" -storage = "1.5.5" -jsoup = "1.18.1" +storage = "2.0.0" +jsoup = "1.20.1" [libraries] +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } -junit = { group = "junit", name = "junit", version.ref = "junit" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } -androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } -androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } storage = { module = "com.anggrayudi:storage", version.ref = "storage" } +junit = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 19cfad9..81aa1c0 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-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From d5eb56c334627ef776205ad236d8c9341f057f17 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Tue, 10 Jun 2025 00:32:16 +0300 Subject: [PATCH 54/57] Fix issues for Android 15 and add dependency to the service --- app/src/main/AndroidManifest.xml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eb6c31c..fb77c9d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,13 @@ - + + + + + + + + android:exported="true" + android:permission="android.permission.INTERNET" + android:process=":remote"> - + - + + From 3c9f87e6afbcb1fd628d4a532bb7e9fa22826c06 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Tue, 10 Jun 2025 00:33:54 +0300 Subject: [PATCH 55/57] Convert an int type field to a long type field --- .../questopiabundle/QuestopiaBundle.kt | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt index c29664a..46514a7 100644 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -27,29 +27,27 @@ class QuestopiaBundle : Service(), GameInterface { private val libCharlieProxy: LibCharlieProxyImpl = LibCharlieProxyImpl(this@QuestopiaBundle, this) private lateinit var callbacks: AsyncCallbacks - @Volatile - private var counterInterval = 500 + @Volatile private var counterInterval = 500L private val counterTask: Runnable = object : Runnable { override fun run() { libAlphaProxy.executeCounter() - counterHandler.postDelayed(this, counterInterval.toLong()) + counterHandler.postDelayed(this, counterInterval) } } private val counterNDKTask: Runnable = object : Runnable { override fun run() { libBravoProxy.executeCounter() - counterNDKHandler.postDelayed(this, counterInterval.toLong()) + counterNDKHandler.postDelayed(this, counterInterval) } } private val counterSNXTask: Runnable = object : Runnable { override fun run() { libCharlieProxy.executeCounter() - counterSNXHandler.postDelayed(this, counterInterval.toLong()) + counterSNXHandler.postDelayed(this, counterInterval) } } - @Volatile - private var mLibVersion = 0 + @Volatile private var mLibVersion = 570 override fun requestReceiveFile(filePath: String): Uri { try { @@ -154,7 +152,7 @@ class QuestopiaBundle : Service(), GameInterface { } override fun setCountInter(delayMillis: Int) { - counterInterval = delayMillis + counterInterval = delayMillis.toLong() } override fun doWithCounterDisabled(runnable: Runnable?) { @@ -162,19 +160,19 @@ class QuestopiaBundle : Service(), GameInterface { 570 -> { counterNDKHandler.removeCallbacks(counterNDKTask) runnable?.run() - counterNDKHandler.postDelayed(counterNDKTask, counterInterval.toLong()) + counterNDKHandler.postDelayed(counterNDKTask, counterInterval) } 575 -> { counterSNXHandler.removeCallbacks(counterSNXTask) runnable?.run() - counterSNXHandler.postDelayed(counterSNXTask, counterInterval.toLong()) + counterSNXHandler.postDelayed(counterSNXTask, counterInterval) } 592 -> { counterHandler.removeCallbacks(counterTask) runnable?.run() - counterHandler.postDelayed(counterTask, counterInterval.toLong()) + counterHandler.postDelayed(counterTask, counterInterval) } } } @@ -200,17 +198,17 @@ class QuestopiaBundle : Service(), GameInterface { mLibVersion = libVer when (mLibVersion) { 570 -> { - counterNDKHandler.postDelayed(counterNDKTask, counterInterval.toLong()) + counterNDKHandler.postDelayed(counterNDKTask, counterInterval) libBravoProxy.startLibThread() } 575 -> { - counterSNXHandler.postDelayed(counterSNXTask, counterInterval.toLong()) + counterSNXHandler.postDelayed(counterSNXTask, counterInterval) libCharlieProxy.startLibThread() } 592 -> { - counterHandler.postDelayed(counterTask, counterInterval.toLong()) + counterHandler.postDelayed(counterTask, counterInterval) libAlphaProxy.startLibThread() } } From dee8e325e15ce8c37f1dd097f68c9fd07329d28f Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Tue, 10 Jun 2025 00:34:41 +0300 Subject: [PATCH 56/57] Remove unused build feature --- app/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c124b99..f0dcf08 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -34,7 +34,6 @@ android { } buildFeatures { - viewBinding = true buildConfig = true aidl = true } From 1a20338cbe9299dcf4e5920bbe45c4208e027142 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Tue, 10 Jun 2025 00:36:17 +0300 Subject: [PATCH 57/57] Bump to 0.1.0 --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f0dcf08..4652d53 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,7 +12,7 @@ android { minSdk = 26 targetSdk = 34 versionCode = 100000 - versionName = "0.0.1" + versionName = "0.1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }