mirror of
https://github.com/izzy2lost/Shipwright-Android.git
synced 2026-07-05 15:19:26 -07:00
FileSelector
This commit is contained in:
@@ -337,7 +337,11 @@ target_include_directories(ZAPDLib PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../libpng
|
||||
)
|
||||
|
||||
target_link_libraries(ZAPDLib PRIVATE png)
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
target_link_libraries(ZAPDLib PRIVATE png log)
|
||||
else()
|
||||
target_link_libraries(ZAPDLib PRIVATE png)
|
||||
endif()
|
||||
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
#include "Utils/Path.h"
|
||||
#include "WarningHandler.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
|
||||
// Define a custom macro to replace printf with __android_log_print
|
||||
#define printf(...) __android_log_print(ANDROID_LOG_INFO, "YourTag", __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
#include "ZAnimation.h"
|
||||
ZNormalAnimation nAnim(nullptr);
|
||||
ZCurveAnimation cAnim(nullptr);
|
||||
@@ -306,6 +314,7 @@ extern "C" int zapd_main(int argc, char* argv[])
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
int fileListSize = fileList.size();
|
||||
Globals::Instance->singleThreaded = false;
|
||||
Globals::Instance->verbosity = VerbosityLevel::VERBOSITY_INFO;
|
||||
|
||||
for (int i = 0; i < fileListSize; i++)
|
||||
Globals::Instance->workerData[i] = new FileWorker();
|
||||
@@ -327,16 +336,15 @@ extern "C" int zapd_main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (!Globals::Instance->singleThreaded)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (numWorkersLeft <= 0)
|
||||
break;
|
||||
if (!Globals::Instance->singleThreaded) {
|
||||
while (true) {
|
||||
printf("%i", numWorkersLeft);
|
||||
if (numWorkersLeft <= 0)
|
||||
break;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
auto diff =
|
||||
@@ -422,9 +430,12 @@ int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileM
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s 1",fileListItem.c_str()); // It's getting stuck here VV. Need to print in Parse?
|
||||
|
||||
parseSuccessful = Parse(fileListItem, Globals::Instance->baseRomPath,
|
||||
Globals::Instance->outputPath, fileMode, workerID);
|
||||
|
||||
printf("%s 2",fileListItem.c_str());
|
||||
if (!parseSuccessful)
|
||||
return 1;
|
||||
|
||||
@@ -443,6 +454,7 @@ int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileM
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s 3",fileListItem.c_str());
|
||||
for (int i = 0; i < Globals::Instance->workerData[workerID]->files.size(); i++)
|
||||
{
|
||||
delete Globals::Instance->workerData[workerID]->files[i];
|
||||
@@ -464,9 +476,11 @@ int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileM
|
||||
bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path& outPath,
|
||||
ZFileMode fileMode, int workerID)
|
||||
{
|
||||
printf("%i parse 1",workerID);
|
||||
tinyxml2::XMLDocument doc;
|
||||
tinyxml2::XMLError eResult = doc.LoadFile(xmlFilePath.string().c_str());
|
||||
|
||||
printf("%i parse 2",workerID);
|
||||
if (eResult != tinyxml2::XML_SUCCESS)
|
||||
{
|
||||
// TODO: use XMLDocument::ErrorIDToName to get more specific error messages here
|
||||
@@ -475,7 +489,9 @@ bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("%i parse 3",workerID);
|
||||
tinyxml2::XMLNode* root = doc.FirstChild();
|
||||
printf("%i parse 4",workerID);
|
||||
|
||||
if (root == nullptr)
|
||||
{
|
||||
@@ -485,6 +501,7 @@ bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("%i parse 5",workerID);
|
||||
for (tinyxml2::XMLElement* child = root->FirstChildElement(); child != NULL;
|
||||
child = child->NextSiblingElement())
|
||||
{
|
||||
@@ -538,6 +555,7 @@ bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path
|
||||
HANDLE_ERROR(WarningType::InvalidXML, errorHeader, errorBody);
|
||||
}
|
||||
}
|
||||
printf("%i parse 6",workerID);
|
||||
|
||||
if (fileMode != ZFileMode::ExternalFile)
|
||||
{
|
||||
@@ -564,6 +582,7 @@ bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path
|
||||
if (exporterSet != nullptr && exporterSet->endXMLFunc != nullptr)
|
||||
exporterSet->endXMLFunc();
|
||||
}
|
||||
printf("%i parse 7",workerID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
#define UNREACHABLE __builtin_unreachable();
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <SDL2/SDL_messagebox.h>
|
||||
@@ -61,27 +66,27 @@ static constexpr uint32_t OOT_PAL_10 = 0xB044B569;
|
||||
static constexpr uint32_t OOT_PAL_11 = 0xB2055FBD;
|
||||
|
||||
static const std::unordered_map<uint32_t, const char*> verMap = {
|
||||
{ OOT_PAL_GC, "PAL Gamecube" },
|
||||
{ OOT_PAL_MQ, "PAL MQ" },
|
||||
{ OOT_PAL_GC_DBG1, "PAL Debug 1" },
|
||||
{ OOT_PAL_GC_DBG2, "PAL Debug 2" },
|
||||
{ OOT_PAL_GC_MQ_DBG, "PAL MQ Debug" },
|
||||
{ OOT_PAL_10, "PAL N64 1.0" },
|
||||
{ OOT_PAL_11, "PAL N64 1.1" },
|
||||
{ OOT_PAL_GC, "PAL Gamecube" },
|
||||
{ OOT_PAL_MQ, "PAL MQ" },
|
||||
{ OOT_PAL_GC_DBG1, "PAL Debug 1" },
|
||||
{ OOT_PAL_GC_DBG2, "PAL Debug 2" },
|
||||
{ OOT_PAL_GC_MQ_DBG, "PAL MQ Debug" },
|
||||
{ OOT_PAL_10, "PAL N64 1.0" },
|
||||
{ OOT_PAL_11, "PAL N64 1.1" },
|
||||
};
|
||||
|
||||
// TODO only check the first 54MB of the rom.
|
||||
static constexpr std::array<const uint32_t, 10> goodCrcs = {
|
||||
0xfa8c0555, // MQ DBG 64MB (Original overdump)
|
||||
0x8652ac4c, // MQ DBG 64MB
|
||||
0x5B8A1EB7, // MQ DBG 64MB (Empty overdump)
|
||||
0x1f731ffe, // MQ DBG 54MB
|
||||
0x044b3982, // NMQ DBG 54MB
|
||||
0xEB15D7B9, // NMQ DBG 64MB
|
||||
0xDA8E61BF, // GC PAL
|
||||
0x7A2FAE68, // GC MQ PAL
|
||||
0xFD9913B1, // N64 PAL 1.0
|
||||
0xE033FBBA, // N64 PAL 1.1
|
||||
0xfa8c0555, // MQ DBG 64MB (Original overdump)
|
||||
0x8652ac4c, // MQ DBG 64MB
|
||||
0x5B8A1EB7, // MQ DBG 64MB (Empty overdump)
|
||||
0x1f731ffe, // MQ DBG 54MB
|
||||
0x044b3982, // NMQ DBG 54MB
|
||||
0xEB15D7B9, // NMQ DBG 64MB
|
||||
0xDA8E61BF, // GC PAL
|
||||
0x7A2FAE68, // GC MQ PAL
|
||||
0xFD9913B1, // N64 PAL 1.0
|
||||
0xE033FBBA, // N64 PAL 1.1
|
||||
};
|
||||
|
||||
enum class ButtonId : int {
|
||||
@@ -90,6 +95,27 @@ enum class ButtonId : int {
|
||||
FIND,
|
||||
};
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
const char* javaRomPath = NULL;
|
||||
bool fileDialogOpen = false;
|
||||
|
||||
//function to be called from C
|
||||
void openFilePickerFromC(JNIEnv* env, jobject javaObject) {
|
||||
fileDialogOpen = true;
|
||||
jclass javaClass = env->GetObjectClass(javaObject);
|
||||
jmethodID openFilePickerMethod = env->GetMethodID(javaClass, "openFilePicker", "()V");
|
||||
env->CallVoidMethod(javaObject, openFilePickerMethod);
|
||||
}
|
||||
// Define the native method to handle the selected file path
|
||||
extern "C" void JNICALL Java_com_dishii_soh_MainActivity_nativeHandleSelectedFile(JNIEnv* env, jobject obj, jstring filePath) {
|
||||
const char* filePathStr = env->GetStringUTFChars(filePath, 0);
|
||||
javaRomPath = strdup(filePathStr); // save filepath to string
|
||||
fileDialogOpen = false;
|
||||
env->ReleaseStringUTFChars(filePath, filePathStr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Extractor::ShowErrorBox(const char* title, const char* text) {
|
||||
#ifdef _WIN32
|
||||
@@ -324,16 +350,35 @@ bool Extractor::GetRomPathFromBox() {
|
||||
return false;
|
||||
}
|
||||
mCurrentRomPath = nameBuffer;
|
||||
#else
|
||||
auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64 *.n64 *.v64" }).result();
|
||||
#else
|
||||
|
||||
#ifndef __ANDROID__
|
||||
auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64 *.n64 *.v64" }).result();
|
||||
#else
|
||||
JNIEnv* javaEnv = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
jobject javaObject = (jobject)SDL_AndroidGetActivity();
|
||||
std::vector<std::string> selection;
|
||||
openFilePickerFromC(javaEnv, javaObject);
|
||||
while(fileDialogOpen){
|
||||
//Do nothing until it's chosen
|
||||
SDL_Delay(250);
|
||||
}
|
||||
SDL_Log("%s",javaRomPath);
|
||||
selection.push_back(javaRomPath);
|
||||
#endif
|
||||
if (selection.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mCurrentRomPath = selection[0];
|
||||
#endif
|
||||
#endif
|
||||
mCurRomSize = GetCurRomSize();
|
||||
#ifdef __ANDROID__
|
||||
if (javaRomPath) {
|
||||
free((void*)javaRomPath);
|
||||
javaRomPath = NULL;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -414,7 +459,7 @@ bool Extractor::ManuallySearchForRomMatchingType(RomSearchMode searchMode) {
|
||||
|
||||
char msgBuf[150];
|
||||
snprintf(msgBuf, 150, "The selected rom does not match the expected game type\nExpected type: %s.\n\nDo you want to search again?",
|
||||
searchMode == RomSearchMode::MQ ? "Master Quest" : "Vanilla");
|
||||
searchMode == RomSearchMode::MQ ? "Master Quest" : "Vanilla");
|
||||
|
||||
while ((searchMode == RomSearchMode::Vanilla && IsMasterQuest()) ||
|
||||
(searchMode == RomSearchMode::MQ && !IsMasterQuest())) {
|
||||
@@ -542,8 +587,12 @@ const char* Extractor::GetZapdVerStr() const {
|
||||
}
|
||||
|
||||
std::string Extractor::Mkdtemp() {
|
||||
std::string temp_dir = SDL_AndroidGetExternalStoragePath();//std::filesystem::temp_directory_path().string();
|
||||
|
||||
#ifndef __ANDROID__
|
||||
std::string temp_dir = std::filesystem::temp_directory_path().string();
|
||||
#else
|
||||
std::string temp_dir = SDL_AndroidGetExternalStoragePath();
|
||||
#endif
|
||||
|
||||
// create 6 random alphanumeric characters
|
||||
static const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
std::random_device rd;
|
||||
@@ -581,9 +630,12 @@ bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
|
||||
std::filesystem::copy(installPath + "/assets", tempdir + "/assets",
|
||||
std::filesystem::copy_options::recursive | std::filesystem::copy_options::update_existing);
|
||||
#else
|
||||
//std::filesystem::create_symlink(installPath + "/assets", tempdir + "/assets");
|
||||
#ifndef __ANDROID__
|
||||
std::filesystem::create_symlink(installPath + "/assets", tempdir + "/assets");
|
||||
#else
|
||||
std::filesystem::copy(installPath + "/assets", tempdir + "/assets",
|
||||
std::filesystem::copy_options::recursive | std::filesystem::copy_options::update_existing);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
std::filesystem::current_path(tempdir);
|
||||
@@ -617,8 +669,26 @@ bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
|
||||
SetWindowPos(cmdWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
int ret;
|
||||
SDL_MessageBoxData boxData = { 0 };
|
||||
SDL_MessageBoxButtonData buttons[2] = { { 0 } };
|
||||
|
||||
buttons[0].buttonid = IDYES;
|
||||
buttons[0].text = "Ok";
|
||||
buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
|
||||
boxData.numbuttons = 1;
|
||||
boxData.flags = SDL_MESSAGEBOX_INFORMATION;
|
||||
boxData.message = "The screen will go black for at least 30 seconds, but it could take up to 3 minutes.";
|
||||
boxData.title = "Starting Extraction";
|
||||
boxData.buttons = buttons;
|
||||
SDL_ShowMessageBox(&boxData, &ret);
|
||||
#endif
|
||||
|
||||
zapd_main(argc, (char**)argv.data());
|
||||
|
||||
SDL_Log("zapd_main finished");
|
||||
|
||||
#ifdef _WIN32
|
||||
// Hide the command window again.
|
||||
ShowWindow(cmdWindow, SW_HIDE);
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
<!-- Allow access to the vibrator -->
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- if you want to capture audio, uncomment this. -->
|
||||
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
|
||||
@@ -80,7 +80,7 @@
|
||||
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:preferMinimalPostProcessing="true"
|
||||
android:exported="true"
|
||||
android:screenOrientation="landscape"
|
||||
android:screenOrientation="userLandscape"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@@ -18,14 +18,54 @@ import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
||||
//This class is the main SDLActivity
|
||||
//This class is the main SDLActivity and just sets up a bunch of default files
|
||||
public class MainActivity extends SDLActivity{
|
||||
|
||||
private static final int STORAGE_PERMISSION_REQUEST_CODE = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
//Check if folder/file exists and if it doesn't, create it
|
||||
// Check if storage permissions are granted
|
||||
if (hasStoragePermission()) {
|
||||
setupFiles();
|
||||
} else {
|
||||
requestStoragePermission();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check if storage permission is granted
|
||||
private boolean hasStoragePermission() {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED &&
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
// Request storage permission
|
||||
private void requestStoragePermission() {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
STORAGE_PERMISSION_REQUEST_CODE);
|
||||
}
|
||||
|
||||
// Handle permission request result
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == STORAGE_PERMISSION_REQUEST_CODE) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
setupFiles();
|
||||
} else {
|
||||
// Permission denied, handle accordingly (e.g., show a message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupFiles(){
|
||||
File externalAssetsDir = new File(getExternalFilesDir(null), "assets");
|
||||
if (!externalAssetsDir.exists()) {
|
||||
try {
|
||||
@@ -57,4 +97,51 @@ public class MainActivity extends SDLActivity{
|
||||
|
||||
}
|
||||
|
||||
private native void nativeHandleSelectedFile(String filePath);
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == 0 && resultCode == RESULT_OK) {
|
||||
Uri selectedFileUri = data.getData();
|
||||
String fileName = "ZELOOTD.z64";
|
||||
File destinationDirectory = getExternalFilesDir(null); // The second argument can specify a subdirectory, or you can pass null to use the root directory.
|
||||
File destinationFile = new File(destinationDirectory, fileName);
|
||||
|
||||
if (destinationDirectory != null) {
|
||||
try {
|
||||
InputStream in = getContentResolver().openInputStream(selectedFileUri);
|
||||
OutputStream out = new FileOutputStream(destinationFile);
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
in.close();
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
nativeHandleSelectedFile(destinationFile.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
public void openFilePicker() {
|
||||
// Create an Intent to open the file picker dialog
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.setType("*/*");
|
||||
|
||||
// Start the file picker dialog
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
// Check if external storage is available and writable
|
||||
private boolean isExternalStorageWritable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user