diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1576bc4b..ec605344 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ name: Build launcher on: workflow_dispatch: push: - branches: ["main"] + branches: '*' jobs: build: diff --git a/.gitignore b/.gitignore index b1dd2417..134c5889 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,7 @@ *.iml .gradle /local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml +/.idea/* .DS_Store /build /captures diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 89f76d0b..00000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -launcher \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 7643783a..00000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c..00000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b589d56e..00000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index ae388c2a..00000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 875a1122..00000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index 217e5c51..00000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 4b7c2b30..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bb597a0b..08e093b0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,7 +9,7 @@ android { defaultConfig { applicationId "com.geode.launcher" minSdk 23 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "0.6.0-alpha.1" @@ -21,8 +21,8 @@ android { arguments "-DDOBBY_DEBUG=OFF" } } - // support only armv7 as GD only supports armv7 - ndkConfig.abiFilters "armeabi-v7a" + + ndkConfig.abiFilters "arm64-v8a", "armeabi-v7a" } buildTypes { @@ -62,14 +62,13 @@ android { dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation "androidx.compose.ui:ui:$compose_version" - implementation 'androidx.compose.material3:material3:1.1.1' + implementation 'androidx.compose.material3:material3:1.1.2' implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' - implementation 'androidx.activity:activity-compose:1.7.2' - implementation 'androidx.activity:activity-ktx:1.7.2' + implementation 'androidx.activity:activity-compose:1.8.2' + implementation 'androidx.activity:activity-ktx:1.8.2' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0' - implementation files('../libs/fmod.jar') + implementation 'com.google.android.material:material:1.11.0' debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" } \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 6292525d..5bf8e297 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -22,4 +22,6 @@ if(NOT dobby_POPULATED) add_subdirectory("${dobby_SOURCE_DIR}" ${dobby_BINARY_DIR} EXCLUDE_FROM_ALL) target_include_directories(launcherfix PRIVATE ${dobby_SOURCE_DIR}/include) target_link_libraries(launcherfix dobby) -endif() \ No newline at end of file +endif() + +target_link_libraries(launcherfix log) \ No newline at end of file diff --git a/app/src/main/cpp/launcher-fix.cpp b/app/src/main/cpp/launcher-fix.cpp index 0b41ee50..bdaca145 100644 --- a/app/src/main/cpp/launcher-fix.cpp +++ b/app/src/main/cpp/launcher-fix.cpp @@ -1,7 +1,13 @@ +#include +#include #include +#include #include +#include + +#ifndef DISABLE_LAUNCHER_FIX #include -#include +#endif class DataPaths { public: @@ -27,6 +33,8 @@ JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setDataPath( auto data_path_str = env->GetStringUTFChars(data_path, &is_copy); DataPaths::get_instance().data_path = std::string(data_path_str); + + env->ReleaseStringUTFChars(data_path, data_path_str); } extern "C" @@ -39,6 +47,38 @@ JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setOriginalDataPath( auto data_path_str = env->GetStringUTFChars(data_path, &is_copy); DataPaths::get_instance().original_data_path = std::string(data_path_str); + + env->ReleaseStringUTFChars(data_path, data_path_str); +} + +extern "C" +JNIEXPORT jboolean JNICALL Java_com_geode_launcher_LauncherFix_loadLibraryFromOffset(JNIEnv *env, jobject, jstring library_name, jint fd, jlong offset) { + // loads a given library at an offset and file descriptor + // assumes we have ownership of the file passed in fd + + auto is_copy = jboolean(); + auto library_cname = env->GetStringUTFChars(library_name, &is_copy); + + android_dlextinfo ext_info{}; + ext_info.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET; + ext_info.library_fd = fd; + ext_info.library_fd_offset = offset; + + auto handle = android_dlopen_ext(library_cname, RTLD_NOW | RTLD_GLOBAL, &ext_info); + env->ReleaseStringUTFChars(library_name, library_cname); + close(fd); + + if (handle == nullptr) { + auto error = dlerror(); + __android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-Fix", "dlopen_ext failed. given: %s\n", error); + + return false; + } + + // we don't need the library anymore + dlclose(handle); + + return true; } FILE* (*fopen_original)(const char *pathname, const char *mode); @@ -60,6 +100,7 @@ FILE* fopen_hook(const char* pathname, const char* mode) { } [[gnu::constructor]] [[gnu::used]] void setup_hooks() { + #ifndef DISABLE_LAUNCHER_FIX auto fopen_addr = dlsym(RTLD_NEXT, "fopen"); DobbyHook( @@ -67,4 +108,5 @@ FILE* fopen_hook(const char* pathname, const char* mode) { reinterpret_cast(&fopen_hook), reinterpret_cast(&fopen_original) ); + #endif } diff --git a/app/src/main/java/com/customRobTop/BaseRobTopActivity.kt b/app/src/main/java/com/customRobTop/BaseRobTopActivity.kt index 2ab4cd7d..f9a1a2bb 100644 --- a/app/src/main/java/com/customRobTop/BaseRobTopActivity.kt +++ b/app/src/main/java/com/customRobTop/BaseRobTopActivity.kt @@ -9,6 +9,7 @@ import android.net.NetworkCapabilities import android.net.Uri import android.provider.Settings import android.util.Log +import android.view.WindowManager import android.widget.Toast import com.customRobTop.JniToCpp.resumeSound import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.closeIMEKeyboard @@ -127,6 +128,11 @@ object BaseRobTopActivity { isLoaded = true } + @JvmStatic + fun getDeviceRefreshRate(): Float { + return (me.get()?.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.refreshRate + } + // Everyplay doesn't even exist anymore lol @JvmStatic fun setupEveryplay() {} diff --git a/app/src/main/java/com/geode/launcher/GeometryDashActivity.kt b/app/src/main/java/com/geode/launcher/GeometryDashActivity.kt index 453ad857..bf82607a 100644 --- a/app/src/main/java/com/geode/launcher/GeometryDashActivity.kt +++ b/app/src/main/java/com/geode/launcher/GeometryDashActivity.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.ActivityInfo +import android.content.pm.PackageInfo import android.os.Build import android.os.Bundle import android.os.Environment @@ -44,7 +45,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL private var mHasWindowFocus = false private var mReceiver: BroadcastReceiver? = null - @SuppressLint("UnsafeDynamicallyLoadedCode") override fun onCreate(savedInstanceState: Bundle?) { setupUIState() @@ -57,7 +57,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL } val gdPackageInfo = packageManager.getPackageInfo(Constants.PACKAGE_NAME, 0) - val gdNativeLibraryPath = "${gdPackageInfo.applicationInfo.nativeLibraryDir}/" try { LaunchUtils.addAssetsFromPackage(assets, gdPackageInfo) @@ -86,8 +85,8 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL Cocos2dxHelper.init(this, this) GeodeUtils.setContext(this) - System.load("$gdNativeLibraryPath/lib${Constants.FMOD_LIB_NAME}.so") - System.load("$gdNativeLibraryPath/lib${Constants.COCOS_LIB_NAME}.so") + tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME) + tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME) if (getLoadTesting()) { loadTestingLibraries() @@ -114,6 +113,44 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL } } + @SuppressLint("UnsafeDynamicallyLoadedCode") + private fun tryLoadLibrary(packageInfo: PackageInfo, libraryName: String) { + try { + val nativeDir = packageInfo.applicationInfo.nativeLibraryDir + System.load("$nativeDir/lib$libraryName.so") + } catch (ule: UnsatisfiedLinkError) { + loadLibraryFromAssets(libraryName) + } + } + + private fun loadLibraryFromAssets(libraryName: String) { + // loads a library loaded in assets (which points to the apk + an offset) + // these libraries are available to the application after merging + + // find the first instance of the library in preferred abi order + val libraryFd = Build.SUPPORTED_ABIS.asSequence() + .mapNotNull { + try { + assets.openNonAssetFd("lib/$it/lib$libraryName.so") + } catch (_: Exception) { + null + } + } + .firstOrNull() ?: throw UnsatisfiedLinkError("Could not find library lib$libraryName.so") + + val fdOffset = libraryFd.startOffset + val fdDescriptor = libraryFd.parcelFileDescriptor.detachFd() + + if (!LauncherFix.loadLibraryFromOffset("lib$libraryName.so", fdDescriptor, fdOffset)) { + throw UnsatisfiedLinkError("Failed to load asset lib$libraryName.so") + } + + // after the library is opened in native code, it should be available for loading + // you can read more about the behavior: + // https://android.googlesource.com/platform/libcore/+/7f3eb2e0ac87bdea471bc577380cf50025aebde5/ojluni/src/main/java/java/lang/Runtime.java#1060 + System.loadLibrary(libraryName) + } + @SuppressLint("UnsafeDynamicallyLoadedCode") private fun loadGeodeLibrary(): Boolean { // Load Geode if exists diff --git a/app/src/main/java/com/geode/launcher/LauncherFix.kt b/app/src/main/java/com/geode/launcher/LauncherFix.kt index b117ebd8..2394a649 100644 --- a/app/src/main/java/com/geode/launcher/LauncherFix.kt +++ b/app/src/main/java/com/geode/launcher/LauncherFix.kt @@ -10,4 +10,6 @@ object LauncherFix { external fun setDataPath(dataPath: String) external fun setOriginalDataPath(dataPath: String) + + external fun loadLibraryFromOffset(libraryName: String, fd: Int, offset: Long): Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/geode/launcher/utils/LaunchUtils.kt b/app/src/main/java/com/geode/launcher/utils/LaunchUtils.kt index 48929847..8701e6a7 100644 --- a/app/src/main/java/com/geode/launcher/utils/LaunchUtils.kt +++ b/app/src/main/java/com/geode/launcher/utils/LaunchUtils.kt @@ -4,7 +4,6 @@ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.res.AssetManager import android.content.Context -import com.geode.launcher.utils.Constants import java.io.File object LaunchUtils { @@ -45,6 +44,10 @@ object LaunchUtils { // (the source recommends replacing with AssetManager.setApkAssets(ApkAssets[], boolean) lol) val clazz = assetManager.javaClass val aspMethod = clazz.getDeclaredMethod("addAssetPath", String::class.java) + aspMethod.invoke(assetManager, packageInfo.applicationInfo.sourceDir) + packageInfo.applicationInfo.splitSourceDirs?.forEach { + aspMethod.invoke(assetManager, it) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/fmod/AudioDevice.kt b/app/src/main/java/org/fmod/AudioDevice.kt new file mode 100644 index 00000000..a67b975e --- /dev/null +++ b/app/src/main/java/org/fmod/AudioDevice.kt @@ -0,0 +1,66 @@ +package org.fmod + +import android.media.AudioTrack +import android.util.Log + +class AudioDevice { + private var mTrack: AudioTrack? = null + private fun fetchChannelConfigFromCount(i: Int): Int { + if (i == 1) { + return 2 + } + if (i == 2) { + return 3 + } + if (i == 6) { + return 252 + } + return if (i == 8) 6396 else 0 + } + + fun init(i: Int, i2: Int, i3: Int, i4: Int): Boolean { + val fetchChannelConfigFromCount = fetchChannelConfigFromCount(i) + val minBufferSize = AudioTrack.getMinBufferSize(i2, fetchChannelConfigFromCount, 2) + if (minBufferSize < 0) { + Log.w( + "fmod", + "AudioDevice::init : Couldn't query minimum buffer size, possibly unsupported sample rate or channel count" + ) + } else { + Log.i("fmod", "AudioDevice::init : Min buffer size: $minBufferSize bytes") + } + val i5 = i3 * i4 * i * 2 + val i6 = if (i5 > minBufferSize) i5 else minBufferSize + Log.i("fmod", "AudioDevice::init : Actual buffer size: $i6 bytes") + return try { + val audioTrack = AudioTrack(3, i2, fetchChannelConfigFromCount, 2, i6, 1) + mTrack = audioTrack + try { + audioTrack.play() + true + } catch (unused: IllegalStateException) { + Log.e("fmod", "AudioDevice::init : AudioTrack play caused IllegalStateException") + mTrack?.release() + mTrack = null + false + } + } catch (unused2: IllegalArgumentException) { + Log.e("fmod", "AudioDevice::init : AudioTrack creation caused IllegalArgumentException") + false + } + } + + fun close() { + try { + mTrack?.stop() + } catch (unused: IllegalStateException) { + Log.e("fmod", "AudioDevice::init : AudioTrack stop caused IllegalStateException") + } + mTrack?.release() + mTrack = null + } + + fun write(sArr: ShortArray?, i: Int) { + mTrack!!.write(sArr!!, 0, i) + } +} diff --git a/app/src/main/java/org/fmod/FMOD.kt b/app/src/main/java/org/fmod/FMOD.kt new file mode 100644 index 00000000..f9837460 --- /dev/null +++ b/app/src/main/java/org/fmod/FMOD.kt @@ -0,0 +1,142 @@ +package org.fmod + +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.res.AssetManager +import android.media.AudioManager +import android.net.Uri +import android.os.Build +import android.util.Log +import java.io.FileNotFoundException + +// i never agreed to your licenses! take that +@SuppressLint("StaticFieldLeak") +object FMOD { + private var gContext: Context? = null + private val gPluginBroadcastReceiver = PluginBroadcastReceiver() + + external fun OutputAAudioHeadphonesChanged() + + @JvmStatic + fun init(context: Context?) { + gContext = context + if (context != null) { + gContext!!.registerReceiver( + gPluginBroadcastReceiver, + IntentFilter("android.intent.action.HEADSET_PLUG") + ) + } + } + + @JvmStatic + fun close() { + val context = gContext + context?.unregisterReceiver(gPluginBroadcastReceiver) + gContext = null + } + + @JvmStatic + fun checkInit(): Boolean { + return gContext != null + } + + @JvmStatic + fun getAssetManager(): AssetManager? { + val context = gContext + return context?.assets + } + + @JvmStatic + fun supportsLowLatency(): Boolean { + val outputBlockSize = getOutputBlockSize() + val lowLatencyFlag = lowLatencyFlag() + val proAudioFlag = proAudioFlag() + val z = outputBlockSize in 1..1024 + val isBluetoothOn = isBluetoothOn() + Log.i( + "fmod", + "FMOD::supportsLowLatency : Low latency = $lowLatencyFlag, Pro Audio = $proAudioFlag, Bluetooth On = $isBluetoothOn, Acceptable Block Size = $z ($outputBlockSize)" + ) + return z && lowLatencyFlag && !isBluetoothOn + } + + @JvmStatic + fun lowLatencyFlag(): Boolean { + if (Build.VERSION.SDK_INT < 5) { + return false + } + + val context = gContext ?: return false + return context.packageManager.hasSystemFeature("android.hardware.audio.low_latency") + } + + @JvmStatic + fun proAudioFlag(): Boolean { + if (Build.VERSION.SDK_INT < 5) { + return false + } + + val context = gContext ?: return false + return context.packageManager.hasSystemFeature("android.hardware.audio.pro") + } + + @JvmStatic + fun supportsAAudio(): Boolean { + return Build.VERSION.SDK_INT >= 27 + } + + @JvmStatic + fun getOutputSampleRate(): Int { + if (Build.VERSION.SDK_INT < 17) { + return 0 + } + + val context = gContext ?: return 0 + val audioService = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val property = audioService.getProperty("android.media.property.OUTPUT_SAMPLE_RATE") + + return property?.toInt() ?: 0 + } + + @JvmStatic + fun getOutputBlockSize(): Int { + if (Build.VERSION.SDK_INT < 17) { + return 0 + } + + val context = gContext ?: return 0 + val audioService = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val property = audioService.getProperty("android.media.property.OUTPUT_FRAMES_PER_BUFFER") + + return property?.toInt() ?: 0 + } + + @JvmStatic + fun isBluetoothOn(): Boolean { + val context = gContext ?: return false + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + return audioManager.isBluetoothA2dpOn || audioManager.isBluetoothScoOn + } + + @JvmStatic + fun fileDescriptorFromUri(str: String?): Int { + gContext?.apply { + try { + contentResolver.openFileDescriptor(Uri.parse(str), "r")?.apply { + return detachFd() + } + } catch (_: FileNotFoundException) { } + } + + return -1 + } + + internal class PluginBroadcastReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + OutputAAudioHeadphonesChanged() + } + } +} diff --git a/app/src/main/java/org/fmod/MediaCodec.kt b/app/src/main/java/org/fmod/MediaCodec.kt new file mode 100644 index 00000000..3dafc1fb --- /dev/null +++ b/app/src/main/java/org/fmod/MediaCodec.kt @@ -0,0 +1,200 @@ +package org.fmod + +import android.media.MediaCrypto +import android.media.MediaDataSource +import android.media.MediaExtractor +import android.media.MediaCodec.BufferInfo +import android.util.Log +import android.view.Surface +import java.io.IOException +import java.nio.ByteBuffer + +class MediaCodec { + private var channelCount = 0 + + var mCodecPtr: Long = 0 + private var mCurrentOutputBufferIndex = -1 + private var mDataSourceProxy: Any? = null + private var mDecoder: android.media.MediaCodec? = null + private var mExtractor: MediaExtractor? = null + private var mInputBuffers: Array? = null + private var mInputFinished = false + private var length: Long = 0 + private var mOutputBuffers: Array? = null + private var mOutputFinished = false + private var sampleRate = 0 + + fun init(j: Long): Boolean { + mCodecPtr = j + var i = 0 + + try { + val mediaExtractor = MediaExtractor() + mExtractor = mediaExtractor + mediaExtractor.setDataSource(object : MediaDataSource() { + override fun close() {} + override fun readAt(j: Long, bArr: ByteArray, i: Int, i2: Int): Int { + return fmodReadAt(mCodecPtr, j, bArr, i, i2) + } + + override fun getSize(): Long { + return fmodGetSize(mCodecPtr) + } + }) + } catch (e5: IOException) { + Log.w("fmod", "MediaCodec::init : $e5") + return false + } + + val trackCount = mExtractor!!.trackCount + var i2 = 0 + while (i2 < trackCount) { + val trackFormat = mExtractor!!.getTrackFormat(i2) + val string = trackFormat.getString("mime") + Log.d( + "fmod", + "MediaCodec::init : Format $i2 / $trackCount -- $trackFormat" + ) + if (string == "audio/mp4a-latm") { + return try { + mDecoder = android.media.MediaCodec.createDecoderByType(string) + mExtractor!!.selectTrack(i2) + mDecoder!!.configure(trackFormat, null as Surface?, null as MediaCrypto?, 0) + mDecoder!!.start() + mInputBuffers = mDecoder!!.inputBuffers + mOutputBuffers = mDecoder!!.outputBuffers + val integer = + if (trackFormat.containsKey("encoder-delay")) trackFormat.getInteger("encoder-delay") else 0 + if (trackFormat.containsKey("encoder-padding")) { + i = trackFormat.getInteger("encoder-padding") + } + val j2 = trackFormat.getLong("durationUs") + channelCount = trackFormat.getInteger("channel-count") + val integer2 = trackFormat.getInteger("sample-rate") + sampleRate = integer2 + length = + (((j2 * integer2.toLong() + 999999) / 1000000).toInt() - integer - i).toLong() + true + } catch (e6: IOException) { + Log.e("fmod", "MediaCodec::init : $e6") + false + } + } else { + i2++ + } + } + return false + } + + fun release() { + val mediaCodec = mDecoder + if (mediaCodec != null) { + mediaCodec.stop() + mDecoder?.release() + mDecoder = null + } + val mediaExtractor = mExtractor + if (mediaExtractor != null) { + mediaExtractor.release() + mExtractor = null + } + } + + fun read(bArr: ByteArray?, i: Int): Int { + var dequeueInputBuffer = 0 + val i2 = + if (!mInputFinished || !mOutputFinished || mCurrentOutputBufferIndex != -1) 0 else -1 + while (!mInputFinished && mDecoder!!.dequeueInputBuffer(0).also { + dequeueInputBuffer = it + } >= 0) { + val readSampleData = mExtractor!!.readSampleData(mInputBuffers!![dequeueInputBuffer], 0) + if (readSampleData >= 0) { + mDecoder!!.queueInputBuffer( + dequeueInputBuffer, + 0, + readSampleData, + mExtractor!!.sampleTime, + 0 + ) + mExtractor!!.advance() + } else { + mDecoder!!.queueInputBuffer(dequeueInputBuffer, 0, 0, 0, 4) + mInputFinished = true + } + } + if (!mOutputFinished && mCurrentOutputBufferIndex == -1) { + val bufferInfo = BufferInfo() + val dequeueOutputBuffer = mDecoder!!.dequeueOutputBuffer(bufferInfo, 10000) + if (dequeueOutputBuffer >= 0) { + mCurrentOutputBufferIndex = dequeueOutputBuffer + mOutputBuffers!![dequeueOutputBuffer].limit(bufferInfo.size) + mOutputBuffers!![dequeueOutputBuffer].position(bufferInfo.offset) + } else if (dequeueOutputBuffer == -3) { + mOutputBuffers = mDecoder!!.outputBuffers + } else if (dequeueOutputBuffer == -2) { + Log.d( + "fmod", + "MediaCodec::read : MediaCodec::dequeueOutputBuffer returned MediaCodec.INFO_OUTPUT_FORMAT_CHANGED " + mDecoder!!.outputFormat + ) + } else if (dequeueOutputBuffer == -1) { + Log.d( + "fmod", + "MediaCodec::read : MediaCodec::dequeueOutputBuffer returned MediaCodec.INFO_TRY_AGAIN_LATER." + ) + } else { + Log.w( + "fmod", + "MediaCodec::read : MediaCodec::dequeueOutputBuffer returned $dequeueOutputBuffer" + ) + } + if (bufferInfo.flags and 4 !== 0) { + mOutputFinished = true + } + } + val i3 = mCurrentOutputBufferIndex + if (i3 == -1) { + return i2 + } + val byteBuffer = mOutputBuffers!![i3] + val min = byteBuffer.remaining().coerceAtMost(i) + byteBuffer[bArr, 0, min] + if (!byteBuffer.hasRemaining()) { + byteBuffer.clear() + mDecoder!!.releaseOutputBuffer(mCurrentOutputBufferIndex, false) + mCurrentOutputBufferIndex = -1 + } + return min + } + + fun seek(i: Int) { + val i2 = mCurrentOutputBufferIndex + if (i2 != -1) { + mOutputBuffers!![i2].clear() + mCurrentOutputBufferIndex = -1 + } + mInputFinished = false + mOutputFinished = false + mDecoder!!.flush() + val j = i.toLong() + mExtractor!!.seekTo(j * 1000000 / sampleRate.toLong(), MediaExtractor.SEEK_TO_PREVIOUS_SYNC) + val sampleTime = (mExtractor!!.sampleTime * sampleRate.toLong() + 999999) / 1000000 + var i3 = ((j - sampleTime) * channelCount.toLong() * 2).toInt() + if (i3 < 0) { + Log.w( + "fmod", + "MediaCodec::seek : Seek to $i resulted in position $sampleTime" + ) + return + } + val bArr = ByteArray(1024) + while (i3 > 0) { + i3 -= read(bArr, Math.min(1024, i3)) + } + } + + companion object { + external fun fmodGetSize(j: Long): Long + + external fun fmodReadAt(j: Long, j2: Long, bArr: ByteArray?, i: Int, i2: Int): Int + } +} diff --git a/build.gradle b/build.gradle index 11c122cc..a0118ae2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ buildscript { ext { - compose_version = '1.5.1' - compose_compiler = '1.4.7' + compose_version = '1.5.4' + compose_compiler = '1.5.7' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '8.1.1' apply false - id 'com.android.library' version '8.1.1' apply false - id 'org.jetbrains.kotlin.android' version '1.8.21' apply false + id 'com.android.application' version '8.2.0' apply false + id 'com.android.library' version '8.2.0' apply false + id 'org.jetbrains.kotlin.android' version '1.9.21' apply false } tasks.register('clean', Delete) { diff --git a/libs/fmod.jar b/libs/fmod.jar deleted file mode 100644 index 2c2ecca5..00000000 Binary files a/libs/fmod.jar and /dev/null differ