diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 0496327..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 100100 - versionName "0.1.0-alpha" - 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..4652d53 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,59 @@ +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.1.0" + + 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 { + 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/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 62fbffa..fb77c9d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,13 @@ - + + + + + + + + android:process=":remote"> - + - + + 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..a39099b 100644 --- a/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl +++ b/app/src/main/aidl/org/qp/android/questopiabundle/AsyncCallbacks.aidl @@ -1,12 +1,12 @@ 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; 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); @@ -17,5 +17,8 @@ interface AsyncCallbacks { void closeFile(String filePath); void playFile(String path, int volume); + Uri requestReceiveFile(in String filePath); + Uri requestCreateFile(in Uri fileUri, String path); + void onError(in LibException libException); } \ No newline at end of file diff --git a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.java b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.java deleted file mode 100644 index ad7af35..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.java +++ /dev/null @@ -1,37 +0,0 @@ -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); - - /** - * Set the counter location processing interval to delayMillis milliseconds. - */ - void setCountInter(int delayMillis); - - /** - * Execute runnable without processing the location counter. - */ - void doWithCounterDisabled(Runnable runnable); - -} diff --git a/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt new file mode 100644 index 0000000..bd948cc --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/GameInterface.kt @@ -0,0 +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 + +interface GameInterface { + fun requestReceiveFile(filePath: String): 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 doUpdateState(request: LibRefIRequest) + fun showLibDialog(dialog: LibTypeDialog?, inputString: String?): LibDialogRetValue? + fun changeVisWindow(type: LibTypeWindow?, show: Boolean) + /** + * Set the counter location processing interval to `delayMillis` milliseconds. + */ + fun setCountInter(delayMillis: Int) + /** + * Execute `runnable` without processing the location counter. + */ + fun doWithCounterDisabled(runnable: Runnable?) +} diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.java b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.java deleted file mode 100644 index 54a0838..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.qp.android.questopiabundle; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -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(); - } - - public LibDialogRetValue() { - - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(outTextValue); - dest.writeInt(outNumValue); - } -} diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt new file mode 100644 index 0000000..00dc3e9 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/LibDialogRetValue.kt @@ -0,0 +1,32 @@ +package org.qp.android.questopiabundle + +import android.os.Parcel +import android.os.Parcelable + +data class LibDialogRetValue( + val outTextValue: String = "", + val outNumValue: Int = 0 +) : Parcelable { + + constructor(source: Parcel) : this( + source.readString() ?: "", + source.readInt() + ) + + override fun describeContents(): Int = 0 + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(outTextValue) + dest.writeInt(outNumValue) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibDialogRetValue { + return LibDialogRetValue(source) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibException.java b/app/src/main/java/org/qp/android/questopiabundle/LibException.java deleted file mode 100644 index dbc3a18..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/LibException.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.qp.android.questopiabundle; - -import android.os.Parcel; -import android.os.Parcelable; - -public class LibException implements 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); - } - - @Override - public LibException[] newArray(int size) { - return new LibException[size]; - } - }; - - 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; - } - - protected LibException(Parcel in) { - errorMessage = in.readString(); - errorCode = in.readInt(); - } - - public Exception toException() { - switch (errorCode) { - case RUNTIME_EXCEPTION: - return new RuntimeException(errorMessage); - case ARITHMETIC_EXCEPTION: - return new ArithmeticException(errorMessage); - default: - return new RuntimeException(errorMessage); - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(errorMessage); - dest.writeInt(errorCode); - } -} diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibException.kt b/app/src/main/java/org/qp/android/questopiabundle/LibException.kt new file mode 100644 index 0000000..f25f2f3 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/LibException.kt @@ -0,0 +1,39 @@ +package org.qp.android.questopiabundle + +import android.os.Parcel +import android.os.Parcelable + +class LibException( + private val errorMessage: String?, + private val errorCode: Int = RUNTIME_EXCEPTION +) : Parcelable { + + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) + + fun toException(): Exception { + return when (errorCode) { + RUNTIME_EXCEPTION -> RuntimeException(errorMessage) + else -> RuntimeException(errorMessage) + } + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(errorMessage) + parcel.writeInt(errorCode) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + const val RUNTIME_EXCEPTION: Int = 1000 + override fun createFromParcel(parcel: Parcel): LibException { + return LibException(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibResult.java b/app/src/main/java/org/qp/android/questopiabundle/LibResult.java deleted file mode 100644 index 349c006..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/LibResult.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.qp.android.questopiabundle; - -import android.os.Parcel; -import android.os.Parcelable; - -public class LibResult implements 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; - } - - protected LibResult(Parcel in) { - this.value = in.readParcelable(getClass().getClassLoader()); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(value, flags); - } - - @Override - public int describeContents() { - return 0; - } -} diff --git a/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt b/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt new file mode 100644 index 0000000..f9583b6 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/LibResult.kt @@ -0,0 +1,29 @@ +package org.qp.android.questopiabundle + +import android.os.Parcel +import android.os.Parcelable + +data class LibResult( + var value: T? +) : Parcelable { + + constructor(source: Parcel) : this(null) { + this.value = source.readParcelable(javaClass.classLoader) + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeParcelable(value, flags) + } + + override fun describeContents(): Int = 0 + + companion object CREATOR : Parcelable.Creator> { + override fun createFromParcel(source: Parcel): LibResult { + return LibResult(source) + } + + override fun newArray(size: Int): Array?> { + return arrayOfNulls(size) + } + } +} diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.java b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.java deleted file mode 100644 index a495682..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.java +++ /dev/null @@ -1,365 +0,0 @@ -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); - } - }; - private final Runnable counterNDKTask = new Runnable() { - @Override - public void run() { - libBravoProxy.executeCounter(); - counterNDKHandler.postDelayed(this, counterInterval); - } - }; - private final Runnable counterSNXTask = new Runnable() { - @Override - public void run() { - libCharlieProxy.executeCounter(); - counterSNXHandler.postDelayed(this, counterInterval); - } - }; - private volatile int mLibVersion; - - @Override - public boolean isPlayingFile(String filePath) { - try { - if (callbacks == null) return false; - return callbacks.isPlayingFile(filePath); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); - return false; - } - } - - @Override - public void closeAllFiles() { - try { - if (callbacks == null) return; - callbacks.closeAllFiles(); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); - } - } - - @Override - public void closeFile(String filePath) { - try { - if (callbacks == null) return; - callbacks.closeFile(filePath); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); - } - } - - @Override - public void playFile(String path, int volume) { - try { - if (callbacks == null) return; - callbacks.playFile(path, volume); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "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); - } - } - - public void removeCallback() { - counterHandler.removeCallbacks(counterTask); - counterNDKHandler.removeCallbacks(counterNDKTask); - counterSNXHandler.removeCallbacks(counterSNXTask); - } - - @Override - public void doChangeCurrGameDir(Uri newGameDirUri) { - if (callbacks == null) return; - - try { - callbacks.sendChangeCurrGameDir(newGameDirUri); - } catch (Exception e) { - Log.e(this.getClass().getSimpleName(), "Error", e); - } - } - - @Override - public void doRefresh(LibRefIRequest request) { - if (callbacks == null) return; - - try { - switch (mLibVersion) { - case 570 -> { - callbacks.sendLibRef(new LibResult<>(request)); - callbacks.sendLibGameState(new LibResult<>(libBravoProxy.getGameState())); - } - case 575 -> { - callbacks.sendLibRef(new LibResult<>(request)); - callbacks.sendLibGameState(new LibResult<>(libCharlieProxy.getGameState())); - } - case 592 -> { - callbacks.sendLibRef(new LibResult<>(request)); - callbacks.sendLibGameState(new LibResult<>(libAlphaProxy.getGameState())); - } - } - } catch (Exception e) { - 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; - } - - try { - return callbacks.doOnShowDialog(new LibResult<>(dialog), inputMessage); - } catch (RemoteException e) { - Log.e("QuestopiaBundle", "Error", e); - } - - if (dialog == LibTypeDialog.DIALOG_MENU) { - wrap.outNumValue = -1; - } else { - wrap.outTextValue = ""; - } - return wrap; - } - - @Override - public void changeVisWindow(LibTypeWindow type, boolean show) { - if (callbacks == null) return; - try { - callbacks.doChangeVisWindow(new LibResult<>(type), show); - } catch (RemoteException e) { - Log.e("QuestopiaBundle", "Error", e); - } - } - - @Override - public void setCountInter(int delayMillis) { - counterInterval = delayMillis; - } - - @Override - public void doWithCounterDisabled(Runnable runnable) { - switch (mLibVersion) { - case 570 -> { - counterNDKHandler.removeCallbacks(counterNDKTask); - runnable.run(); - counterNDKHandler.postDelayed(counterNDKTask, counterInterval); - } - case 575 -> { - counterSNXHandler.removeCallbacks(counterSNXTask); - runnable.run(); - counterSNXHandler.postDelayed(counterSNXTask, counterInterval); - } - case 592 -> { - counterHandler.removeCallbacks(counterTask); - runnable.run(); - counterHandler.postDelayed(counterTask, counterInterval); - } - } - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return new IQuestopiaBundle.Stub() { - @Override - public String versionPlugin() throws RemoteException { - return BuildConfig.VERSION_NAME; - } - - @Override - public String titlePlugin() throws RemoteException { - return "Questopia Bundle"; - } - - @Override - public String authorPlugin() throws RemoteException { - return "l3ger0j"; - } - - @Override - public void startNativeLib(int libVer) throws RemoteException { - mLibVersion = libVer; - switch (mLibVersion) { - case 570 -> { - libBravoProxy = new LibBravoProxyImpl(QuestopiaBundle.this); - setCallback(); - - libBravoProxy.setGameInterface(QuestopiaBundle.this); - libBravoProxy.startLibThread(); - } - case 575 -> { - libCharlieProxy = new LibCharlieProxyImpl(QuestopiaBundle.this); - setCallback(); - - libCharlieProxy.setGameInterface(QuestopiaBundle.this); - libCharlieProxy.startLibThread(); - } - case 592 -> { - libAlphaProxy = new LibAlphaProxyImpl(QuestopiaBundle.this); - setCallback(); - - libAlphaProxy.setGameInterface(QuestopiaBundle.this); - libAlphaProxy.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; - } - } - 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); - } - } - - @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); - } - } - - @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); - } - } - - @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); - } - } - 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); - } - } - 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); - } - } - } - } - - @Override - public void sendAsync(AsyncCallbacks callbacks) throws RemoteException { - QuestopiaBundle.this.callbacks = callbacks; - } - }; - } - - @Override - public void onDestroy() { - super.onDestroy(); - libAlphaProxy = null; - libBravoProxy = null; - libCharlieProxy = null; - removeCallback(); - } -} diff --git a/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt new file mode 100644 index 0000000..46514a7 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/QuestopiaBundle.kt @@ -0,0 +1,328 @@ +package org.qp.android.questopiabundle + +import android.app.Service +import android.content.Intent +import android.net.Uri +import android.os.IBinder +import android.os.Looper +import android.os.RemoteException +import android.util.Log +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 +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 = 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 private var counterInterval = 500L + private val counterTask: Runnable = object : Runnable { + override fun run() { + libAlphaProxy.executeCounter() + counterHandler.postDelayed(this, counterInterval) + } + } + private val counterNDKTask: Runnable = object : Runnable { + override fun run() { + libBravoProxy.executeCounter() + counterNDKHandler.postDelayed(this, counterInterval) + } + } + private val counterSNXTask: Runnable = object : Runnable { + override fun run() { + libCharlieProxy.executeCounter() + counterSNXHandler.postDelayed(this, counterInterval) + } + } + + @Volatile private var mLibVersion = 570 + + override fun requestReceiveFile(filePath: String): Uri { + try { + return callbacks.requestReceiveFile(filePath) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + return Uri.EMPTY + } + } + + override fun requestCreateFile(dirUri: Uri, path: String): Uri { + try { + return callbacks.requestCreateFile(dirUri, path) ?: Uri.EMPTY + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + return Uri.EMPTY + } + } + + override fun isPlayingFile(filePath: String): Boolean { + try { + return callbacks.isPlayingFile(filePath) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + return false + } + } + + override fun closeAllFiles() { + try { + callbacks.closeAllFiles() + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + } + } + + override fun closeFile(filePath: String?) { + try { + callbacks.closeFile(filePath) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + } + } + + override fun playFile(path: String?, volume: Int) { + try { + callbacks.playFile(path, volume) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + } + } + + private fun removeCallback() { + counterHandler.removeCallbacks(counterTask) + counterNDKHandler.removeCallbacks(counterNDKTask) + counterSNXHandler.removeCallbacks(counterSNXTask) + } + + override fun doChangeCurrGameDir(newGameDirUri: Uri?) { + try { + callbacks.sendChangeCurrGameDir(newGameDirUri) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "Error", e) + } + } + + override fun doUpdateState(request: LibRefIRequest) { + try { + when (mLibVersion) { + 570 -> { + callbacks.updateState(LibResult(request), LibResult(libBravoProxy.gameState)) + } + + 575 -> { + callbacks.updateState(LibResult(request), LibResult(libCharlieProxy.gameState)) + } + + 592 -> { + callbacks.updateState(LibResult(request), LibResult(libAlphaProxy.gameState)) + } + } + } catch (e: Exception) { + Log.e("QuestopiaBundle", "Error", e) + } + } + + override fun showLibDialog(dialog: LibTypeDialog?, inputString: String?): LibDialogRetValue? { + try { + return callbacks.doOnShowDialog(LibResult(dialog), inputString) + } catch (e: RemoteException) { + Log.e("QuestopiaBundle", "Error", e) + return LibDialogRetValue() + } + } + + override fun changeVisWindow(type: LibTypeWindow?, show: Boolean) { + try { + callbacks.doChangeVisWindow(LibResult(type), show) + } catch (e: RemoteException) { + Log.e("QuestopiaBundle", "Error", e) + } + } + + override fun setCountInter(delayMillis: Int) { + counterInterval = delayMillis.toLong() + } + + override fun doWithCounterDisabled(runnable: Runnable?) { + when (mLibVersion) { + 570 -> { + counterNDKHandler.removeCallbacks(counterNDKTask) + runnable?.run() + counterNDKHandler.postDelayed(counterNDKTask, counterInterval) + } + + 575 -> { + counterSNXHandler.removeCallbacks(counterSNXTask) + runnable?.run() + counterSNXHandler.postDelayed(counterSNXTask, counterInterval) + } + + 592 -> { + counterHandler.removeCallbacks(counterTask) + runnable?.run() + counterHandler.postDelayed(counterTask, counterInterval) + } + } + } + + override fun onBind(intent: Intent): IBinder = object : IQuestopiaBundle.Stub() { + @Throws(RemoteException::class) + override fun versionPlugin(): String { + return BuildConfig.VERSION_NAME + } + + @Throws(RemoteException::class) + override fun titlePlugin(): String { + return "Questopia Bundle" + } + + @Throws(RemoteException::class) + override fun authorPlugin(): String { + return "l3ger0j" + } + + @Throws(RemoteException::class) + override fun startNativeLib(libVer: Int) { + mLibVersion = libVer + when (mLibVersion) { + 570 -> { + counterNDKHandler.postDelayed(counterNDKTask, counterInterval) + libBravoProxy.startLibThread() + } + + 575 -> { + counterSNXHandler.postDelayed(counterSNXTask, counterInterval) + libCharlieProxy.startLibThread() + } + + 592 -> { + counterHandler.postDelayed(counterTask, counterInterval) + libAlphaProxy.startLibThread() + } + } + } + + @Throws(RemoteException::class) + override fun stopNativeLib(libVer: Int) { + mLibVersion = libVer + when (mLibVersion) { + 570 -> libBravoProxy.stopLibThread() + + 575 -> libCharlieProxy.stopLibThread() + + 592 -> libAlphaProxy.stopLibThread() + } + removeCallback() + } + + @Throws(RemoteException::class) + override fun runGameIntoLib( + gameId: Long, + gameTitle: String, + gameDirUri: Uri, + gameFileUri: Uri + ) { + when (mLibVersion) { + 570 -> libBravoProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri) + + 575 -> libCharlieProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri) + + 592 -> libAlphaProxy.runGame(gameId, gameTitle, gameDirUri, gameFileUri) + } + } + + @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) + } + } + + @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) + } + } + + 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) + } + } + + 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) + } + } + } + } + + @Throws(RemoteException::class) + override fun sendAsync(callbacks: AsyncCallbacks?) { + this@QuestopiaBundle.callbacks = callbacks ?: return + } + } + + override fun onDestroy() { + super.onDestroy() + removeCallback() + } +} diff --git a/app/src/main/java/org/qp/android/questopiabundle/dto/LibGameState.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGameState.kt new file mode 100644 index 0000000..e368e17 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGameState.kt @@ -0,0 +1,61 @@ +package org.qp.android.questopiabundle.dto + +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable + +data class LibGameState( + 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( + source.readParcelable(LibIConfig::class.java.classLoader) ?: LibIConfig(), + source.readInt() != 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, LibGenItem) + source.readTypedList(objectsList, LibGenItem) + source.readTypedList(menuItemsList, LibGenItem) + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeParcelable(interfaceConfig, flags) + dest.writeInt(if (gameRunning) 1 else 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 + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibGameState { + return LibGameState(source) + } + + 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/dto/LibGenItem.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGenItem.kt new file mode 100644 index 0000000..4cbb3dd --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibGenItem.kt @@ -0,0 +1,32 @@ +package org.qp.android.questopiabundle.dto + +import android.os.Parcel +import android.os.Parcelable + +data class LibGenItem( + val text: String = "", + val imagePath: String = "" +) : Parcelable { + + constructor(source: Parcel) : this( + source.readString() ?: "", + source.readString() ?: "" + ) + + override fun describeContents(): Int = 0 + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(text) + dest.writeString(imagePath) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibGenItem { + return LibGenItem(source) + } + + 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/dto/LibIConfig.kt b/app/src/main/java/org/qp/android/questopiabundle/dto/LibIConfig.kt new file mode 100644 index 0000000..2c42d33 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/dto/LibIConfig.kt @@ -0,0 +1,41 @@ +package org.qp.android.questopiabundle.dto + +import android.os.Parcel +import android.os.Parcelable + +data class LibIConfig( + 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( + source.readInt() != 0, + source.readLong(), + source.readLong(), + source.readLong(), + source.readLong() + ) + + override fun describeContents(): Int = 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) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibIConfig { + return LibIConfig(source) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} 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.java deleted file mode 100644 index b1119d2..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibListItem.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.qp.android.questopiabundle.dto; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import com.libqsp.jni.QSPLib; - -import org.libsnxqsp.jni.SNXLib; - -import java.util.Objects; - -public class LibListItem implements Parcelable { - - 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; - } - - public LibListItem(SNXLib.ListItem item) { - this.pathToImage = item.image(); - this.text = item.text(); - } - - 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; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(text); - dest.writeString(pathToImage); - } -} 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.java deleted file mode 100644 index 9da7211..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/dto/LibMenuItem.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.qp.android.questopiabundle.dto; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public class LibMenuItem implements Parcelable { - - 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]; - } - }; - - public String name; - public String pathToImage; - - public LibMenuItem() { - this.name = ""; - this.pathToImage = ""; - } - - protected LibMenuItem(Parcel in) { - this.name = in.readString(); - this.pathToImage = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name); - dest.writeString(pathToImage); - } -} 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.java deleted file mode 100644 index e1ae6cc..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.qp.android.questopiabundle.lib; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public enum LibGameRequest implements Parcelable { - LOAD_FILE, - SAVE_FILE, - USE_EXECUTOR, - USE_INPUT, - RESTART_GAME, - 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 - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name()); - } -} 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 new file mode 100644 index 0000000..dad489d --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameRequest.kt @@ -0,0 +1,32 @@ +package org.qp.android.questopiabundle.lib + +import android.os.Parcel +import android.os.Parcelable + +enum class LibGameRequest : Parcelable { + LOAD_FILE, + SAVE_FILE, + USE_EXECUTOR, + USE_INPUT, + RESTART_GAME, + EXECUTE_CODE, + ; + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + 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.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.java deleted file mode 100644 index 348d971..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibGameState.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.qp.android.questopiabundle.lib; - -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 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 = ""; - } - - 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 - 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 - public int describeContents() { - return 0; - } - - 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<>(); - } - -} 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.java deleted file mode 100644 index 73e136e..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -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() { - } - - protected LibIConfig(Parcel in) { - useHtml = in.readByte() != 0; - fontSize = in.readLong(); - backColor = in.readLong(); - fontColor = in.readLong(); - linkColor = in.readLong(); - } - - public void reset() { - useHtml = false; - fontSize = 0; - backColor = 0; - fontColor = 0; - linkColor = 0; - } - - @Override - public int describeContents() { - return 0; - } - - @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); - } -} 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.java deleted file mode 100644 index 97d4ef2..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.qp.android.questopiabundle.lib; - -import android.net.Uri; - -import org.qp.android.questopiabundle.GameInterface; - -public interface LibIProxy { - /** - * Starts the library thread. - */ - void 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(); - - /** - * Starts execution of the specified line of code in the library. - */ - void execute(String code); - - /** - * Starts processing the location counter in the library. - */ - void executeCounter(); - - LibGameState getGameState(); - - void setGameInterface(GameInterface view); - -} 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 new file mode 100644 index 0000000..e1ff611 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibIProxy.kt @@ -0,0 +1,35 @@ +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 { + /** + * Starts the library thread. + */ + fun startLibThread() + /** + * Stops the library thread. + */ + 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. + */ + fun execute(code: String?) + /** + * Starts processing the location counter in the library. + */ + fun executeCounter() + val gameState: LibGameState + val gameInterface: GameInterface +} 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.java deleted file mode 100644 index 21d7ae9..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.qp.android.questopiabundle.lib; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public class LibRefIRequest implements Parcelable { - - public static final Creator CREATOR = new Creator<>() { - @Override - public LibRefIRequest createFromParcel(Parcel in) { - return new LibRefIRequest(in); - } - - @Override - public LibRefIRequest[] newArray(int size) { - return new LibRefIRequest[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/LibRefIRequest.kt b/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt new file mode 100644 index 0000000..8680082 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibRefIRequest.kt @@ -0,0 +1,41 @@ +package org.qp.android.questopiabundle.lib + +import android.os.Parcel +import android.os.Parcelable + +data class LibRefIRequest( + 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( + 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) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibRefIRequest { + return LibRefIRequest(source) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} 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.java deleted file mode 100644 index 1bc3bd0..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.qp.android.questopiabundle.lib; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public enum LibTypeDialog implements Parcelable { - DIALOG_ERROR, - DIALOG_PICTURE, - DIALOG_MESSAGE, - DIALOG_INPUT, - DIALOG_EXECUTOR, - DIALOG_MENU, - DIALOG_POPUP_SAVE, - DIALOG_POPUP_LOAD, - ; - - - public static final Creator CREATOR = new Creator<>() { - @Override - public LibTypeDialog createFromParcel(Parcel in) { - return LibTypeDialog.valueOf(in.readString()); - } - - @Override - public LibTypeDialog[] newArray(int size) { - return new LibTypeDialog[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name()); - } -} 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 new file mode 100644 index 0000000..7f84b3e --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeDialog.kt @@ -0,0 +1,32 @@ +package org.qp.android.questopiabundle.lib + +import android.os.Parcel +import android.os.Parcelable + +enum class LibTypeDialog : Parcelable { + DIALOG_ERROR, + DIALOG_PICTURE, + DIALOG_MESSAGE, + DIALOG_INPUT, + DIALOG_EXECUTOR, + DIALOG_MENU, + DIALOG_POPUP_SAVE, + DIALOG_POPUP_LOAD, + ; + + override fun describeContents(): Int = 0 + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(name) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel): LibTypeDialog { + 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/LibTypeWindow.java b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.java deleted file mode 100644 index 14cf717..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.qp.android.questopiabundle.lib; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public enum LibTypeWindow implements 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 - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name()); - } -} 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 new file mode 100644 index 0000000..52cc83f --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/LibTypeWindow.kt @@ -0,0 +1,28 @@ +package org.qp.android.questopiabundle.lib + +import android.os.Parcel +import android.os.Parcelable + +enum class LibTypeWindow : Parcelable { + ACTIONS, + OBJECTS, + VARIABLES, + INPUT + ; + + override fun describeContents(): Int = 0 + + override fun writeToParcel(dest: Parcel, flags: Int) { + 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.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java deleted file mode 100644 index 4cb4c85..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.java +++ /dev/null @@ -1,605 +0,0 @@ -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 void runOnQspThread(final Runnable runnable) { -// throwIfNotMainThread(); - if (libThread == null) { - Log.w(TAG, "Lib thread has not been started!"); - return; - } - if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!"); - return; - } - var mLibHandler = libHandler; - if (mLibHandler == null) return; - mLibHandler.post(() -> { - libLock.lock(); - try { - runnable.run(); - } finally { - libLock.unlock(); - } - }); - } - - private boolean loadGameWorld() { - var gameFileUri = gameState.gameFileUri; - var gameData = getFileContents(context, gameFileUri); - if (gameData == null) return false; - - if (!loadGameWorldFromData(gameData, true)) { - showLastQspError(); - Log.d("QSP", "World is not loaded!"); - return false; - } - Log.d("QSP", "World is loaded!"); - 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); - } - } - - /** - * Loads the interface configuration - using HTML, font and colors - from the library. - * - * @return true if the configuration has changed, otherwise false - */ - private boolean loadInterfaceConfiguration() { - var config = gameState.interfaceConfig; - boolean changed = false; - - var htmlResult = getNumVarValue("USEHTML", 0); - var useHtml = htmlResult != 0; - if (config.useHtml != useHtml) { - config.useHtml = useHtml; - changed = true; - } - - var fSizeResult = getNumVarValue("FSIZE", 0); - if (config.fontSize != fSizeResult) { - config.fontSize = fSizeResult; - changed = true; - } - - var bColorResult = getNumVarValue("BCOLOR", 0); - if (config.backColor != bColorResult) { - config.backColor = bColorResult; - changed = true; - } - - var fColorResult = getNumVarValue("FCOLOR", 0); - if (config.fontColor != fColorResult) { - config.fontColor = fColorResult; - changed = true; - } - - var lColorResult = getNumVarValue("LCOLOR", 0); - if (config.linkColor != lColorResult) { - 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()); - } else { - newElement.pathToImage = null; - } - } - 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(); - - for (var element : getObjects()) { - var newElement = new LibListItem(element); - if (newElement.text.contains(" { - while (!Thread.currentThread().isInterrupted()) { - try { - init(); - if (Looper.myLooper() == null) { - 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(); - } - } - }, "libQSP"); - libThread.start(); - } - - public void stopLibThread() { -// throwIfNotMainThread(); - if (libThread == null) return; - if (libThreadInit) { - var handler = libHandler; - if (handler != null) { - handler.getLooper().quitSafely(); - } - libThreadInit = false; - } else { - Log.w(TAG, "libqsp thread has been started, but not initialized"); - } - libThread.interrupt(); - } - - public void enableDebugMode(boolean isDebug) { - runOnQspThread(() -> enableDebugMode(isDebug)); - } - - @Override - public void runGame(long gameId, - String gameTitle, - Uri gameDirUri, - Uri gameFileUri) { - runOnQspThread(() -> doRunGame(gameId, gameTitle, gameDirUri, gameFileUri)); - } - - @Override - public void 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; - if (!restartGame(true)) { - showLastQspError(); - } - }); - } - - @Override - public void loadGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> loadGameState(uri)); - return; - } - final var gameData = getFileContents(context, uri); - if (gameData == null) return; - if (!openSavedGameFromData(gameData, true)) { - showLastQspError(); - } - } - - @Override - public void saveGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> saveGameState(uri)); - return; - } - final var gameData = saveGameAsData(false); - if (gameData == null) return; - writeFileContents(context, uri, gameData); - } - - @Override - public void onActionClicked(final int index) { - runOnQspThread(() -> { - if (!setSelActIndex(index, false)) { - showLastQspError(); - } - if (!execSelAction(true)) { - showLastQspError(); - } - }); - } - - @Override - public void onObjectSelected(final int index) { - runOnQspThread(() -> { - if (!setSelObjIndex(index, true)) { - 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); - if (!execUserInput(true)) { - 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; - if (!execString(input, true)) { - showLastQspError(); - } - }); - } - - @Override - public void execute(final String code) { - runOnQspThread(() -> { - if (!execString(code, true)) { - showLastQspError(); - } - }); - } - - @Override - public void executeCounter() { - if (libLock.isLocked()) return; - runOnQspThread(() -> { - if (!execCounter(true)) { - showLastQspError(); - } - }); - } - - @Override - public LibGameState getGameState() { - return gameState; - } - - @Override - public void setGameInterface(GameInterface inter) { - this.gameInterface = inter; - } - - // endregion LibQpProxy - - // region LibQpCallbacks - - @Override - public void onRefreshInt(boolean isForced) { - var request = new LibRefIRequest(); - - if (loadInterfaceConfiguration()) { - request.isIConfigChanged = true; - } - if (isMainDescChanged()) { - if (isNotEmptyOrBlank(gameState.mainDesc)) { - if (!gameState.mainDesc.equals(getMainDesc())) { - gameState.mainDesc = getMainDesc(); - request.isMainDescChanged = true; - } - } else { - gameState.mainDesc = getMainDesc(); - 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 (isObjsChanged()) { - if (gameState.objectsList != null) { - if (gameState.objectsList != getObjectsList()) { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; - } - } else { - gameState.objectsList = getObjectsList(); - request.isObjectsChanged = true; - } - } - if (isVarsDescChanged()) { - if (isNotEmptyOrBlank(gameState.varsDesc)) { - if (!gameState.varsDesc.equals(getVarsDesc())) { - gameState.varsDesc = getVarsDesc(); - request.isVarsDescChanged = true; - } - } else { - gameState.varsDesc = getVarsDesc(); - request.isVarsDescChanged = true; - } - } - - var inter = gameInterface; - if (inter != null) { - inter.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 - public void onSetTimer(int msecs) { - var inter = gameInterface; - if (inter == null) return; - inter.setCountInter(msecs); - } - - @Override - public void onShowMessage(String text) { - var inter = gameInterface; - if (inter == null) return; - inter.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 - public boolean onIsPlayingFile(String file) { - if (gameInterface == null) return false; - return isNotEmptyOrBlank(file) && gameInterface.isPlayingFile(file); - } - - @Override - public void onCloseFile(String file) { - if (gameInterface == null) return; - if (isNotEmptyOrBlank(file)) { - gameInterface.closeFile(file); - } else { - gameInterface.closeAllFiles(); - } - } - - @Override - public void onOpenGameStatus(String file) { - if (file == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); - } else { - try { - var saveFile = fromFullPath(context, file, 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) { - gameInterface.doWithCounterDisabled(() -> loadGameState(saveFile.getUri())); - } - } catch (Exception e) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()); - } - Log.e(TAG, "Error: ", e); - } - } - } - - @Override - public void onSaveGameStatus(String file) { - if (file == null) { - if (gameInterface == null) return; - 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()); - } else { - if (gameInterface != null) { - 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 - public int onGetMsCount() { - var now = SystemClock.elapsedRealtime(); - if (lastMsCountCallTime == 0) { - lastMsCountCallTime = gameStartTime; - } - var dt = now - lastMsCountCallTime; - lastMsCountCallTime = now; - return (int) dt; - } - - @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; - if (result != -1) { - return result; - } - return super.onShowMenu(items); - } - - @Override - public void onSleep(int msecs) { - try { - Thread.sleep(msecs); - } catch (InterruptedException ex) { - 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 - public void onOpenGame(String file, boolean isNewGame) { - var newGameDir = fromFullPath(context, file, getCurGameDir()); - if (newGameDir == null || !newGameDir.exists()) { - 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); - } - } - - // endregion LibQpCallbacks -} 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 new file mode 100644 index 0000000..4c896e6 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibAlphaProxyImpl.kt @@ -0,0 +1,486 @@ +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 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 +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.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.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.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.concurrent.locks.ReentrantLock +import kotlin.concurrent.Volatile +import kotlin.concurrent.withLock +import kotlin.contracts.ExperimentalContracts + +class LibAlphaProxyImpl( + private val context: Context, + override var gameInterface: GameInterface, + override var gameState: LibGameState = LibGameState(), + private var gameRequest: LibRefIRequest = LibRefIRequest() +) : QSPLib(), LibIProxy { + + 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 val currGameDir: DocumentFile? + get() = fromUri(context, gameState.gameDirUri) + + private fun runOnQspThread(runnable: Runnable) { + if (libThreadInit) { + libHandler.post { + libLock.withLock { runnable.run() } + } + } + } + + private fun loadGameWorld(): Boolean { + val gameFileUri = gameState.gameFileUri + val gameData = getFileContents(context, gameFileUri) ?: return false + + if (!loadGameWorldFromData(gameData, true)) { + showLastQspError() + return false + } + return true + } + + private fun showLastQspError() { + val errorData = lastErrorData + val locName = getStringOrEmpty(errorData.locName) + val desc = getStringOrEmpty(getErrorDesc(errorData.errorNum)) + + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, """ + Location: $locName + Action: ${errorData.actIndex} + Line: ${errorData.intLineNum} + Error number: ${errorData.errorNum} + Description: $desc + """.trimIndent()) + } + + /** + * Loads the interface configuration - using HTML, font and colors - from the library. + * + * @return `true` if the configuration has changed, otherwise `false` + */ + private fun loadInterfaceConfiguration(): Boolean { + val htmlResult = getNumVarValue("USEHTML", 0) + val fSizeResult = getNumVarValue("FSIZE", 0) + val bColorResult = getNumVarValue("BCOLOR", 0) + val fColorResult = getNumVarValue("FCOLOR", 0) + val lColorResult = getNumVarValue("LCOLOR", 0) + + 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 + } + } + + @OptIn(ExperimentalContracts::class) + private val actionsList: List + get() { + if (!isWritableDir(context, currGameDir)) return emptyList() + val actions = mutableListOf() + val gameDir = currGameDir + + for (element in getActions()) { + if (element == null) continue + 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() + } + } + + actions.add(LibGenItem(tempText, tempImagePath)) + } + + return actions + } + + @OptIn(ExperimentalContracts::class) + private val objectsList: List + get() { + if (!isWritableDir(context, currGameDir)) return emptyList() + val objects = mutableListOf() + val gameDir = currGameDir + + for (element in getObjects()) { + if (element == null) continue + var tempImagePath = element.image ?: "" + val tempText = element.name ?: "" + + if (tempText.contains(" newState + false -> gameState + } + } + + 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) + override fun onShowImage(file: String) { + if (!isNotEmptyOrBlank(file)) return + gameInterface.showLibDialog(LibTypeDialog.DIALOG_PICTURE, file) + } + + override fun onSetTimer(msecs: Int) { + gameInterface.setCountInter(msecs) + } + + override fun onShowMessage(text: String) { + 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) + } else { + gameInterface.closeAllFiles() + } + } + + @OptIn(ExperimentalContracts::class) + override fun onOpenGameStatus(file: String?) { + if (file == null) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null) + } else { + try { + val saveFile = gameInterface.requestReceiveFile(file).toDocumentFile(context) + if (isWritableFile(context, saveFile)) { + gameInterface.doWithCounterDisabled { loadGameState(saveFile.uri) } + } else { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Save file not found") + } + } catch (e: Exception) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()) + } + } + } + + override fun onSaveGameStatus(file: String?) { + if (file == null) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_SAVE, null) + } else { + val currGameDir = currGameDir ?: return + val saveFileUri = gameInterface.requestCreateFile(currGameDir.uri, file) + if (saveFileUri != Uri.EMPTY) { + saveGameState(saveFileUri) + } else { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, "Error access dir") + } + } + } + + override fun onInputBox(text: String): String { + val doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_INPUT, text) ?: return "" + return doShow.outTextValue + } + + override fun onGetMsCount(): Int { + val now = SystemClock.elapsedRealtime() + if (lastMsCountCallTime == 0L) { + lastMsCountCallTime = gameStartTime + } + val dt = now - lastMsCountCallTime + lastMsCountCallTime = now + return dt.toInt() + } + + 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 super.onShowMenu(items) + } + + override fun onSleep(msecs: Int) { + try { + Thread.sleep(msecs.toLong()) + } catch (ex: InterruptedException) { + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, ex.toString()) + } + } + + override fun onShowWindow(type: Int, toShow: Boolean) { + val windowType = LibTypeWindow.entries[type] + gameInterface.changeVisWindow(windowType, toShow) + } + + @OptIn(ExperimentalContracts::class) + override fun onOpenGame(file: String, isNewGame: Boolean) { + 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 + } + 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.java b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java deleted file mode 100644 index c873eef..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.java +++ /dev/null @@ -1,640 +0,0 @@ -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 void runOnQspThread(final Runnable runnable) { -// throwIfNotMainThread(); - if (libThread == null) { - Log.w(TAG, "Lib thread has not been started!"); - return; - } - if (!libThreadInit) { - Log.w(TAG, "Lib thread has been started, but not initialized!"); - return; - } - var mLibHandler = libHandler; - if (mLibHandler == null) return; - mLibHandler.post(() -> { - libLock.lock(); - try { - runnable.run(); - } finally { - 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; - - 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); - } - } - - /** - * Loads the interface configuration - using HTML, font and colors - from the library. - * - * @return true if the configuration has changed, otherwise false - */ - private boolean loadInterfaceConfiguration() { - var config = gameState.interfaceConfig; - boolean changed = false; - - var htmlResult = (VarValResp) QSPGetVarValues("USEHTML", 0); - if (htmlResult.isSuccess()) { - boolean useHtml = htmlResult.intValue() != 0; - if (config.useHtml != useHtml) { - config.useHtml = useHtml; - changed = true; - } - } - var fSizeResult = (VarValResp) QSPGetVarValues("FSIZE", 0); - if (fSizeResult.isSuccess() && config.fontSize != fSizeResult.intValue()) { - config.fontSize = fSizeResult.intValue(); - changed = true; - } - var bColorResult = (VarValResp) QSPGetVarValues("BCOLOR", 0); - if (bColorResult.isSuccess() && config.backColor != bColorResult.intValue()) { - config.backColor = bColorResult.intValue(); - changed = true; - } - var fColorResult = (VarValResp) QSPGetVarValues("FCOLOR", 0); - if (fColorResult.isSuccess() && config.fontColor != fColorResult.intValue()) { - config.fontColor = fColorResult.intValue(); - changed = true; - } - var lColorResult = (VarValResp) QSPGetVarValues("LCOLOR", 0); - if (lColorResult.isSuccess() && config.linkColor != lColorResult.intValue()) { - config.linkColor = lColorResult.intValue(); - 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()); - } else { - action.pathToImage = null; - } - } - action.text = gameState.interfaceConfig.useHtml - ? removeHtmlTags(actionResult.text) - : actionResult.text; - - actions.add(action); - } - - 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(" { - while (!Thread.currentThread().isInterrupted()) { - try { - QSPInit(); - if (Looper.myLooper() == null) { - 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(); - } - } - }, "libQSP"); - libThread.start(); - } - - public void stopLibThread() { -// throwIfNotMainThread(); - if (libThread == null) return; - if (libThreadInit) { - var handler = libHandler; - if (handler != null) { - handler.getLooper().quitSafely(); - } - libThreadInit = false; - } else { - Log.w(TAG, "libqsp thread has been started, but not initialized"); - } - libThread.interrupt(); - } - - public void enableDebugMode(boolean isDebug) { - runOnQspThread(() -> enableDebugMode(isDebug)); - } - - @Override - public void runGame(long gameId, - String gameTitle, - Uri gameDirUri, - Uri gameFileUri) { - runOnQspThread(() -> doRunGame(gameId, gameTitle, gameDirUri, gameFileUri)); - } - - @Override - public void 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; - if (!QSPRestartGame(true)) { - showLastQspError(); - } - }); - } - - @Override - public void loadGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> loadGameState(uri)); - return; - } - final var gameData = getFileContents(context, uri); - if (gameData == null) return; - if (!QSPOpenSavedGameFromData(gameData, gameData.length, true)) { - showLastQspError(); - } - } - - @Override - public void saveGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> saveGameState(uri)); - return; - } - final var gameData = QSPSaveGameAsData(false); - if (gameData == null) return; - writeFileContents(context, uri, gameData); - } - - @Override - public void onActionClicked(final int index) { - runOnQspThread(() -> { - if (!QSPSetSelActionIndex(index, false)) { - showLastQspError(); - } - if (!QSPExecuteSelActionCode(true)) { - showLastQspError(); - } - }); - } - - @Override - public void onObjectSelected(final int index) { - runOnQspThread(() -> { - if (!QSPSetSelObjectIndex(index, true)) { - 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); - if (!QSPExecUserInput(true)) { - 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; - if (!QSPExecString(input, true)) { - showLastQspError(); - } - }); - } - - @Override - public void execute(final String code) { - runOnQspThread(() -> { - if (!QSPExecString(code, true)) { - showLastQspError(); - } - }); - } - - @Override - public void executeCounter() { - if (libLock.isLocked()) return; - runOnQspThread(() -> { - if (!QSPExecCounter(true)) { - showLastQspError(); - } - }); - } - - @Override - public LibGameState getGameState() { - return gameState; - } - - @Override - public void setGameInterface(GameInterface inter) { - this.gameInterface = inter; - } - - // endregion LibQpProxy - - // region LibQpCallbacks - - @Override - public void RefreshInt() { - var request = new LibRefIRequest(); - var configChanged = loadInterfaceConfiguration(); - - if (configChanged) { - 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 (QSPIsActionsChanged()) { - if (gameState.actionsList != null) { - if (gameState.actionsList != getActionsList()) { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; - } - } else { - gameState.actionsList = getActionsList(); - 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 (QSPIsVarsDescChanged()) { - if (gameState.varsDesc != null) { - if (!gameState.varsDesc.equals(QSPGetVarsDesc())) { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; - } - } else { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; - } - } - - var inter = gameInterface; - if (inter != null) { - 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 - public void SetTimer(int msecs) { - var inter = gameInterface; - if (inter == null) return; - inter.setCountInter(msecs); - } - - @Override - public void ShowMessage(String message) { - var inter = gameInterface; - if (inter == null) return; - 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 - public boolean IsPlayingFile(final String path) { - if (gameInterface == null) return false; - return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path); - } - - @Override - public void CloseFile(String path) { - if (gameInterface == null) return; - if (isNotEmptyOrBlank(path)) { - gameInterface.closeFile(path); - } else { - gameInterface.closeAllFiles(); - } - } - - @Override - public void OpenGame(String filename) { - if (filename == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); - } else { - 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) { - gameInterface.doWithCounterDisabled(() -> loadGameState(saveFile.getUri())); - } - } catch (Exception e) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()); - } - Log.e(TAG, "Error: ", e); - } - } - } - - @Override - public void SaveGame(String filename) { - if (filename == null) { - if (gameInterface == null) return; - 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()); - } else { - if (gameInterface != null) { - 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 - public int GetMSCount() { - var now = SystemClock.elapsedRealtime(); - if (lastMsCountCallTime == 0) { - lastMsCountCallTime = gameStartTime; - } - var dt = (int) (now - lastMsCountCallTime); - 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 - public void ShowMenu() { - if (gameInterface == null) return; - var doShow = gameInterface.showLibDialog(LibTypeDialog.DIALOG_MENU, null); - if (doShow == null) return; - var result = doShow.outNumValue; - if (result != -1) { - QSPSelectMenuItem(result); - } - } - - @Override - public void DeleteMenu() { - gameState.menuItemsList.clear(); - } - - @Override - public void Wait(int msecs) { - try { - Thread.sleep(msecs); - } catch (InterruptedException ex) { - 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 - public byte[] GetFileContents(String path) { - var targetFile = fromFullPath(context, path, getCurGameDir()); - if (targetFile == null) return null; - var targetFileUri = targetFile.getUri(); - return getFileContents(context , targetFileUri); - } - - @Override - public void ChangeQuestPath(String path) { - var newGameDir = fromFullPath(context, path , getCurGameDir()); - if (newGameDir == null || !newGameDir.exists()) { - 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); - } - } - - // 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 new file mode 100644 index 0000000..c57033d --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibBravoProxyImpl.kt @@ -0,0 +1,504 @@ +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 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.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.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.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.concurrent.locks.ReentrantLock +import kotlin.concurrent.Volatile +import kotlin.concurrent.withLock +import kotlin.contracts.ExperimentalContracts + +class LibBravoProxyImpl( + private val context: Context, + override var gameInterface: GameInterface, + override var gameState: LibGameState = LibGameState(), + private var gameRequest: LibRefIRequest = LibRefIRequest() +) : NDKLib(), LibIProxy { + + 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 val currGameDir: DocumentFile? + get() = fromUri(context, gameState.gameDirUri) + + private fun runOnQspThread(runnable: Runnable) { + if (libThreadInit) { + libHandler.post { + libLock.withLock { runnable.run() } + } + } + } + + private fun loadGameWorld(): Boolean { + val gameFileUri = gameState.gameFileUri + val gameFile = fromUri(context, gameState.gameFileUri) ?: return false + val gameFileFullPath = documentWrap(gameFile).getAbsolutePath(context) + val gameData = getFileContents(context, gameFileUri) ?: return false + + if (!QSPLoadGameWorldFromData(gameData, gameFileFullPath)) { + showLastQspError() + return false + } + return true + } + + private fun showLastQspError() { + val errorData = QSPGetLastErrorData() as ErrorData + val locName = getStringOrEmpty(errorData.locName) + val desc = getStringOrEmpty(QSPGetErrorDesc(errorData.errorNum)) + + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, """ + Location: $locName + Action: ${errorData.index} + Line: ${errorData.line} + Error number: ${errorData.errorNum} + Description: $desc + """.trimIndent()) + } + + /** + * Loads the interface configuration - using HTML, font and colors - from the library. + * + * @return `true` if the configuration has changed, otherwise `false` + */ + private fun loadInterfaceConfiguration(): Boolean { + val oldConfig = gameState.interfaceConfig + + val htmlResult = QSPGetVarValues("USEHTML", 0) as VarValResp + val fSizeResult = QSPGetVarValues("FSIZE", 0) as VarValResp + val bColorResult = QSPGetVarValues("BCOLOR", 0) as VarValResp + val fColorResult = QSPGetVarValues("FCOLOR", 0) as VarValResp + val lColorResult = QSPGetVarValues("LCOLOR", 0) as VarValResp + + 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) + private val actionsList: List + get() { + val gameDir = currGameDir + if (!isWritableDir(context, gameDir)) return emptyList() + + val actions = mutableListOf() + + for (element in QSPGetActionData() ?: return emptyList()) { + if (element == null) continue + 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() + } + } + + actions.add(LibGenItem(tempText, tempImagePath)) + } + + return actions + } + + @OptIn(ExperimentalContracts::class) + private val objectsList: List + get() { + val gameDir = currGameDir + if (!isWritableDir(context, gameDir)) return emptyList() + + val objects = mutableListOf() + for (element in QSPGetObjectData() ?: return emptyList()) { + if (element == null) continue + var tempImagePath = element.image ?: "" + val tempText = element.text ?: "" + + if (tempText.contains(" { - libLock.lock(); - try { - runnable.run(); - } finally { - 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; - - if (!loadGameWorldFromData(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 = 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); - } - } - - /** - * Loads the interface configuration - using HTML, font and colors - from the library. - * - * @return true if the configuration has changed, otherwise false - */ - private boolean loadInterfaceConfiguration() { - var config = gameState.interfaceConfig; - boolean changed = false; - - var htmlResult = (SNXLib.VarValResp) QSPGetVarValues("USEHTML", 0); - if (htmlResult.isSuccess()) { - boolean useHtml = htmlResult.intValue() != 0; - if (config.useHtml != useHtml) { - 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; - } - var bColorResult = (SNXLib.VarValResp) QSPGetVarValues("BCOLOR", 0); - if (bColorResult.isSuccess() && config.backColor != bColorResult.intValue()) { - config.backColor = bColorResult.intValue(); - changed = true; - } - var fColorResult = (SNXLib.VarValResp) QSPGetVarValues("FCOLOR", 0); - if (fColorResult.isSuccess() && config.fontColor != fColorResult.intValue()) { - config.fontColor = fColorResult.intValue(); - changed = true; - } - var lColorResult = (SNXLib.VarValResp) QSPGetVarValues("LCOLOR", 0); - if (lColorResult.isSuccess() && config.linkColor != lColorResult.intValue()) { - config.linkColor = lColorResult.intValue(); - 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()); - } else { - newElement.pathToImage = null; - } - } - 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(); - - for (var element : getObjects()) { - var object = new LibListItem(element); - if (object.text.contains(" { - while (!Thread.currentThread().isInterrupted()) { - try { - init(); - if (Looper.myLooper() == null) { - 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(); - } - } - }, "libQSP"); - libThread.start(); - } - - public void stopLibThread() { -// throwIfNotMainThread(); - if (libThread == null) return; - if (libThreadInit) { - var handler = libHandler; - if (handler != null) { - handler.getLooper().quitSafely(); - } - libThreadInit = false; - } else { - Log.w(TAG, "libqsp thread has been started, but not initialized"); - } - libThread.interrupt(); - } - - public void enableDebugMode(boolean isDebug) { - runOnQspThread(() -> enableDebugMode(isDebug)); - } - - @Override - public void runGame(long gameId, - String gameTitle, - Uri gameDirUri, - Uri gameFileUri) { - runOnQspThread(() -> doRunGame(gameId, gameTitle, gameDirUri, gameFileUri)); - } - - @Override - public void 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; - if (!QSPRestartGame(true)) { - showLastQspError(); - } - }); - } - - @Override - public void loadGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> loadGameState(uri)); - return; - } - final var gameData = getFileContents(context, uri); - if (gameData == null) return; - if (!QSPOpenSavedGameFromData(gameData, gameData.length, true)) { - showLastQspError(); - } - } - - @Override - public void saveGameState(final Uri uri) { - if (!isSameThread(libHandler.getLooper().getThread())) { - runOnQspThread(() -> saveGameState(uri)); - return; - } - final var gameData = QSPSaveGameAsData(false); - if (gameData == null) return; - writeFileContents(context, uri, gameData); - } - - @Override - public void onActionClicked(final int index) { - runOnQspThread(() -> { - if (!setSelActionIndex(index, false)) { - showLastQspError(); - } - if (!executeSelActionCode(true)) { - showLastQspError(); - } - }); - } - - @Override - public void onObjectSelected(final int index) { - runOnQspThread(() -> { - if (!setSelObjectIndex(index, true)) { - 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); - if (!execUserInput(true)) { - 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; - if (!execString(input, true)) { - showLastQspError(); - } - }); - } - - @Override - public void execute(final String code) { - runOnQspThread(() -> { - if (!execString(code, true)) { - showLastQspError(); - } - }); - } - - @Override - public void executeCounter() { - if (libLock.isLocked()) return; - runOnQspThread(() -> { - if (!execCounter(true)) { - showLastQspError(); - } - }); - } - - @Override - public LibGameState getGameState() { - return gameState; - } - - @Override - public void setGameInterface(GameInterface inter) { - this.gameInterface = inter; - } - - // endregion LibQpProxy - - // region LibQpCallbacks - - @Override - public void RefreshInt() { - var request = new LibRefIRequest(); - var configChanged = loadInterfaceConfiguration(); - - if (configChanged) { - 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 (isActionsChanged()) { - if (gameState.actionsList != null) { - if (gameState.actionsList != getActionsList()) { - gameState.actionsList = getActionsList(); - request.isActionsChanged = true; - } - } else { - gameState.actionsList = getActionsList(); - 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 (QSPIsVarsDescChanged()) { - if (gameState.varsDesc != null) { - if (!gameState.varsDesc.equals(QSPGetVarsDesc())) { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; - } - } else { - gameState.varsDesc = QSPGetVarsDesc(); - request.isVarsDescChanged = true; - } - } - - var inter = gameInterface; - if (inter != null) { - 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 - public void SetTimer(int msecs) { - var inter = gameInterface; - if (inter == null) return; - inter.setCountInter(msecs); - } - - @Override - public void ShowMessage(String message) { - var inter = gameInterface; - if (inter == null) return; - 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 - public boolean IsPlayingFile(final String path) { - if (gameInterface == null) return false; - return isNotEmptyOrBlank(path) && gameInterface.isPlayingFile(path); - } - - @Override - public void CloseFile(String path) { - if (gameInterface == null) return; - if (isNotEmptyOrBlank(path)) { - gameInterface.closeFile(path); - } else { - gameInterface.closeAllFiles(); - } - } - - @Override - public void OpenGame(String filename) { - if (filename == null) { - if (gameInterface == null) return; - gameInterface.showLibDialog(LibTypeDialog.DIALOG_POPUP_LOAD, null); - } else { - 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) { - gameInterface.doWithCounterDisabled(() -> loadGameState(saveFile.getUri())); - } - } catch (Exception e) { - if (gameInterface != null) { - gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, e.toString()); - } - Log.e(TAG, "Error: ", e); - } - } - } - - @Override - public void SaveGame(String filename) { - if (filename == null) { - if (gameInterface == null) return; - 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()); - } else { - if (gameInterface != null) { - 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 - public int GetMSCount() { - var now = SystemClock.elapsedRealtime(); - if (lastMsCountCallTime == 0) { - lastMsCountCallTime = gameStartTime; - } - var dt = (int) (now - lastMsCountCallTime); - 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 - 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; - if (result != -1) { - return result; - } - return super.showMenu(); - } - - @Override - public void deleteMenu() { - gameState.menuItemsList.clear(); - } - - @Override - public void Wait(int msecs) { - try { - Thread.sleep(msecs); - } catch (InterruptedException ex) { - 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); - } - - // 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 new file mode 100644 index 0000000..1208597 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/lib/impl/LibCharlieProxyImpl.kt @@ -0,0 +1,480 @@ +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 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 +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.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.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.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.concurrent.locks.ReentrantLock +import kotlin.concurrent.Volatile +import kotlin.concurrent.withLock +import kotlin.contracts.ExperimentalContracts + +class LibCharlieProxyImpl( + private val context: Context, + override var gameInterface: GameInterface, + override var gameState: LibGameState = LibGameState(), + private var gameRequest: LibRefIRequest = LibRefIRequest() +) : SNXLib(), LibIProxy { + + 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 val currGameDir: DocumentFile? + get() = fromUri(context, gameState.gameDirUri) + + private fun runOnQspThread(runnable: Runnable) { + if (libThreadInit) { + libHandler.post { + libLock.withLock { runnable.run() } + } + } + } + + private fun loadGameWorld(): Boolean { + val gameFileUri = gameState.gameFileUri + 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)) { + showLastQspError() + return false + } + + return true + } + + private fun showLastQspError() { + val errorData = lastErrorData + val locName = getStringOrEmpty(errorData.locName) + val desc = getStringOrEmpty(getErrorDesc(errorData.errorNum)) + + gameInterface.showLibDialog(LibTypeDialog.DIALOG_ERROR, """ + Location: $locName + Action: ${errorData.index} + Line: ${errorData.line} + Error number: ${errorData.errorNum} + Description: $desc + """.trimIndent()) + } + + /** + * Loads the interface configuration - using HTML, font and colors - from the library. + * + * @return `true` if the configuration has changed, otherwise `false` + */ + private fun loadInterfaceConfiguration(): Boolean { + val oldConfig = gameState.interfaceConfig + + val htmlResult = getVarValues("USEHTML", 0) + val fSizeResult = getVarValues("FSIZE", 0) + val bColorResult = getVarValues("BCOLOR", 0) + val fColorResult = getVarValues("FCOLOR", 0) + val lColorResult = getVarValues("LCOLOR", 0) + + 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) + private val actionsList: List + get() { + if (!isWritableDir(context, currGameDir)) return emptyList() + val actions = mutableListOf() + val gameDir = currGameDir + + for (element in getActions()) { + if (element == null) continue + 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() + } + } + + actions.add(LibGenItem(tempText, tempImagePath)) + } + + return actions + } + + @OptIn(ExperimentalContracts::class) + private val objectsList: List + get() { + val gameDir = currGameDir + if (!isWritableDir(context, gameDir)) return emptyList() + + val objects = mutableListOf() + for (element in getObjects()) { + if (element == null) continue + var tempImagePath = element.image ?: "" + val tempText = element.text ?: "" + + if (tempText.contains(" + ByteArrayOutputStream().use { out -> + if (`in` != null) { + copy(`in`, out) + } else { + throw NullPointerException() + } + return out.toByteArray() + } + } + } catch (ex: Exception) { + // TODO: 04.12.2024 Add logger + Log.e("FileUtil", "Error reading file: $uriContent", ex) + return null + } + } + + fun documentWrap(inputFile: DocumentFile): FileWrapper.Document { + return FileWrapper.Document(inputFile) + } + + 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 (ex: IOException) { + // TODO: 04.12.2024 Add logger + Log.e("FileUtil", "Error reading file: $uriContent", ex) + } + } +} 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.java deleted file mode 100644 index 91ab5cb..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.java +++ /dev/null @@ -1,51 +0,0 @@ -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 java.util.regex.Pattern; - -public final class HtmlUtil { - - private static final Pattern HTML_PATTERN = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>"); - - public static boolean isContainsHtmlTags(String text) { - 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"); - } - - /** - * Remove HTML tags from the html string and return the resulting string. - */ - public static String removeHtmlTags(String html) { - if (isNullOrEmpty(html)) return ""; - - var result = new StringBuilder(); - var len = html.length(); - var fromIdx = 0; - - while (fromIdx < len) { - var idx = html.indexOf('<', fromIdx); - if (idx == -1) { - result.append(html.substring(fromIdx)); - break; - } - result.append(html, fromIdx, idx); - var endIdx = html.indexOf('>', idx + 1); - if (endIdx == -1) { - return Jsoup.clean(html, Safelist.none()); - } - fromIdx = endIdx + 1; - } - - return result.toString(); - } -} 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 new file mode 100644 index 0000000..47429aa --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/HtmlUtil.kt @@ -0,0 +1,47 @@ +package org.qp.android.questopiabundle.utils + +import org.jsoup.Jsoup +import org.jsoup.safety.Safelist +import org.qp.android.questopiabundle.utils.StringUtil.isNullOrEmpty +import java.util.regex.Pattern + +object HtmlUtil { + private val HTML_PATTERN: Pattern = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>") + + fun isContainsHtmlTags(text: String): Boolean { + return HTML_PATTERN.matcher(text).find() + } + + 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. + */ + fun removeHtmlTags(html: String): String { + if (isNullOrEmpty(html)) return "" + + val result = StringBuilder() + val len = html.length + var fromIdx = 0 + + while (fromIdx < len) { + val idx = html.indexOf('<', fromIdx) + if (idx == -1) { + result.append(html.substring(fromIdx)) + break + } + result.append(html, fromIdx, idx) + val endIdx = html.indexOf('>', idx + 1) + if (endIdx == -1) { + return Jsoup.clean(html, Safelist.none()) + } + fromIdx = endIdx + 1 + } + + return result.toString() + } +} 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.java deleted file mode 100644 index 4a6a8f4..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.qp.android.questopiabundle.utils; - -import androidx.annotation.NonNull; - -public final class PathUtil { - - @NonNull - public static String getFilename(@NonNull String path) { - var idx = path.lastIndexOf('/'); - return idx == -1 ? path : path.substring(idx + 1); - } - - /** - * Leads to the normal form of the path to the game resource (melodies, images). - * - * @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; - if (result.startsWith("./")) { - result = result.substring(2); - } - return result.replace("\\", "/"); - } -} 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 new file mode 100644 index 0000000..9d48bbe --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/PathUtil.kt @@ -0,0 +1,22 @@ +package org.qp.android.questopiabundle.utils + +object PathUtil { + fun getFilename(path: String): String { + val idx = path.lastIndexOf('/') + return if (idx == -1) path else path.substring(idx + 1) + } + + /** + * Leads to the normal form of the path to the game resource (melodies, images). + * + * @implNote Removes "./" from the beginning of the path, replaces all occurrences of "\" with "/". + */ + fun normalizeContentPath(path: String?): String { + if (path == null) return "" + var result: String = path + if (result.startsWith("./")) { + result = result.substring(2) + } + return result.replace("\\", "/") + } +} 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.java deleted file mode 100644 index 069d776..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.qp.android.questopiabundle.utils; - -import androidx.annotation.NonNull; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public final class StreamUtil { - private static final int BUFFER_SIZE = 8192; - - /** - * 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); - } - } -} 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 new file mode 100644 index 0000000..e9142b7 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StreamUtil.kt @@ -0,0 +1,21 @@ +package org.qp.android.questopiabundle.utils + +import java.io.IOException +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. + */ + @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.java b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java deleted file mode 100644 index acf3854..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.qp.android.questopiabundle.utils; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.Contract; - -public final class StringUtil { - - 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(); - } - - @NonNull - @Contract(value = "!null -> param1", pure = true) - public static String getStringOrEmpty(String str) { - return str != null ? str : ""; - } -} 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 new file mode 100644 index 0000000..6b26c8c --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/StringUtil.kt @@ -0,0 +1,24 @@ +package org.qp.android.questopiabundle.utils + +import org.jetbrains.annotations.Contract +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +object StringUtil { + @ExperimentalContracts + fun isNotEmptyOrBlank(str: String?): Boolean { + contract { + returns() implies (str != null) + } + return !str.isNullOrEmpty() && str.isNotBlank() + } + + fun isNullOrEmpty(str: String?): Boolean { + return str.isNullOrEmpty() + } + + @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.java b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java deleted file mode 100644 index bed23e1..0000000 --- a/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.qp.android.questopiabundle.utils; - -import android.os.Handler; -import android.os.Looper; - -public final class ThreadUtil { - - /** - * @return true if the current thread is thread, otherwise false - */ - 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(); - } - } - -} 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 new file mode 100644 index 0000000..744f863 --- /dev/null +++ b/app/src/main/java/org/qp/android/questopiabundle/utils/ThreadUtil.kt @@ -0,0 +1,10 @@ +package org.qp.android.questopiabundle.utils + +object ThreadUtil { + /** + * @return `true` if the current thread is `thread`, otherwise `false` + */ + fun isSameThread(thread: Thread): Boolean { + return Thread.currentThread() == thread + } +} 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..f331f14 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,24 +1,28 @@ [versions] -agp = "8.7.3" -junit = "4.13.2" +agp = "8.10.1" +kotlin = "2.0.21" +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] -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-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } -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" } \ 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 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..b4a6011 --- /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("-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/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/libbravo/src/main/java/org/qp/android/questopiabundle/libbravo/NDKLib.java b/libbravo/src/main/java/org/libndkqsp/jni/NDKLib.java similarity index 81% 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..b5b5eb9 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) { } @@ -30,7 +18,7 @@ public record ErrorData(String locName , int errorNum , int index , int line) { public native void QSPDeInit(); public native boolean QSPIsInCallBack(); public native void QSPEnableDebugMode(boolean isDebug); - public native Object QSPGetCurStateData();//!!!STUB + public native ExecutionState QSPGetCurStateData();//!!!STUB public native String QSPGetVersion(); public native String QSPGetCompiledDateTime(); public native int QSPGetFullRefreshCount(); @@ -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..b2f4aae 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, qspCurObjects[i].Image, qspCurObjects[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 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/libcharlie/src/main/cpp/src/bindings/android/android_control.c b/libcharlie/src/main/cpp/src/bindings/android/android_control.c index ed286cb..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 */ @@ -85,13 +80,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 */ @@ -121,7 +116,7 @@ JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_getMainDesc(JNIEnv *env, } /* The ability to change the text of the main description */ -JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPIsMainDescChanged(JNIEnv *env, jobject this) +JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_isMainDescChanged(JNIEnv *env, jobject this) { return qspIsMainDescChanged; } @@ -129,13 +124,13 @@ JNIEXPORT jboolean JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPIsMainDescChanged(JN /* Additional description of the location */ /* Text of the additional location description window */ -JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_QSPGetVarsDesc(JNIEnv *env, jobject this) +JNIEXPORT jstring JNICALL Java_org_libsnxqsp_jni_SNXLib_getVarsDesc(JNIEnv *env, jobject this) { return snxToJavaString(env, qspCurVars); } /* 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; } @@ -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(); @@ -753,25 +673,25 @@ JNIEXPORT void JNICALL Java_org_libsnxqsp_jni_SNXLib_init(JNIEnv *env, jobject t snxVarValResp = (jclass)(*env)->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/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 03976a9..470d1b2 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"); @@ -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(); @@ -27,18 +26,17 @@ 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 boolean QSPIsVarsDescChanged(); + 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,31 +56,28 @@ 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 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 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