Skip to content

Commit 9fbaad6

Browse files
committed
Add experimental Android app shell
1 parent b03c652 commit 9fbaad6

39 files changed

+6545
-27
lines changed

CMakePresets.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,45 @@
279279
}
280280
}
281281
},
282+
{
283+
"name": "android-base",
284+
"hidden": true,
285+
"generator": "Ninja",
286+
"binaryDir": "${sourceDir}/build/${presetName}",
287+
"cacheVariables": {
288+
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
289+
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
290+
"CMAKE_TOOLCHAIN_FILE": "$env{ANDROID_HOME}/ndk/$env{ANDROID_NDK_VERSION}/build/cmake/android.toolchain.cmake",
291+
"ANDROID_PLATFORM": "android-24"
292+
},
293+
"vendor": {
294+
"microsoft.com/VisualStudioSettings/CMake/1.0": {
295+
"hostOS": [
296+
"Linux"
297+
]
298+
}
299+
}
300+
},
301+
{
302+
"name": "android-arm64",
303+
"displayName": "Android (arm64-v8a)",
304+
"inherits": [
305+
"android-base"
306+
],
307+
"cacheVariables": {
308+
"ANDROID_ABI": "arm64-v8a"
309+
}
310+
},
311+
{
312+
"name": "android-x86_64",
313+
"displayName": "Android (x86_64)",
314+
"inherits": [
315+
"android-base"
316+
],
317+
"cacheVariables": {
318+
"ANDROID_ABI": "x86_64"
319+
}
320+
},
282321
{
283322
"name": "x-linux-ci",
284323
"hidden": true,
@@ -396,6 +435,24 @@
396435
"metaforce"
397436
]
398437
},
438+
{
439+
"name": "android-arm64",
440+
"configurePreset": "android-arm64",
441+
"description": "Android arm64-v8a release build with debug info",
442+
"displayName": "Android arm64-v8a RelWithDebInfo",
443+
"targets": [
444+
"metaforce"
445+
]
446+
},
447+
{
448+
"name": "android-x86_64",
449+
"configurePreset": "android-x86_64",
450+
"description": "Android x86_64 release build with debug info",
451+
"displayName": "Android x86_64 RelWithDebInfo",
452+
"targets": [
453+
"metaforce"
454+
]
455+
},
399456
{
400457
"name": "windows-msvc-debug",
401458
"configurePreset": "windows-msvc-debug",

Runtime/CDvdFile.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
// #include <optick.h>
44

5+
#include <SDL3/SDL_error.h>
56
#include <SDL3/SDL_iostream.h>
67

78
#include <cstring>
89
#include <limits>
910
#include <new>
1011

1112
#include "Runtime/CDvdRequest.hpp"
13+
#include "Runtime/Logging.hpp"
1214
#include "Runtime/CStopwatch.hpp"
1315

1416
namespace metaforce {
@@ -99,10 +101,9 @@ class CFileDvdRequest : public IDvdRequest {
99101
ESeekOrigin m_whence;
100102
int m_offset;
101103

102-
103104
bool m_cancel = false;
104105
bool m_complete = false;
105-
106+
106107
std::function<void(u32)> m_callback;
107108

108109
public:
@@ -185,6 +186,7 @@ class CFileDvdRequest : public IDvdRequest {
185186

186187
std::vector<std::shared_ptr<IDvdRequest>> CDvdFile::m_RequestQueue;
187188
std::string CDvdFile::m_rootDirectory;
189+
std::string CDvdFile::m_lastError;
188190
std::unique_ptr<u8[]> CDvdFile::m_dolBuf;
189191
size_t CDvdFile::m_dolBufLen = 0;
190192

@@ -369,16 +371,21 @@ bool CDvdFile::LoadDolBuf() {
369371

370372
bool CDvdFile::Initialize(const std::string_view& path) {
371373
Shutdown();
374+
m_lastError.clear();
372375

373376
std::string pathStr(path);
374377
SDL_IOStream* io = SDL_IOFromFile(pathStr.c_str(), "rb");
375378
if (io == nullptr) {
379+
m_lastError = std::string{"SDL_IOFromFile failed: "} + SDL_GetError();
380+
spdlog::error("{} (path: '{}')", m_lastError, pathStr);
376381
return false;
377382
}
378383

379384
auto* streamCtx = new (std::nothrow) SDLDiscStreamCtx{io};
380385
if (streamCtx == nullptr) {
381386
SDL_CloseIO(io);
387+
m_lastError = "Failed to allocate SDL disc stream context";
388+
spdlog::error("{} (path: '{}')", m_lastError, pathStr);
382389
return false;
383390
}
384391

@@ -394,7 +401,12 @@ bool CDvdFile::Initialize(const std::string_view& path) {
394401
};
395402
const NodResult discResult = nod_disc_open_stream(&stream, &discOpts, &discRaw);
396403
if (discResult != NOD_RESULT_OK || discRaw == nullptr) {
397-
sdlStreamClose(streamCtx);
404+
const char* nodError = nod_error_message();
405+
m_lastError = fmt::format("nod_disc_open_stream failed ({}){}", int(discResult),
406+
nodError != nullptr && nodError[0] != '\0' ? fmt::format(": {}", nodError) : "");
407+
spdlog::error("{} (path: '{}')", m_lastError, pathStr);
408+
// Ownership of streamCtx is transferred to nod_disc_open_stream.
409+
// FfiDiscStream drops and invokes close() on failure paths.
398410
return false;
399411
}
400412
m_DvdRoot = NodHandleUnique(discRaw, nod_free);
@@ -403,12 +415,24 @@ bool CDvdFile::Initialize(const std::string_view& path) {
403415
const NodResult partitionResult =
404416
nod_disc_open_partition_kind(m_DvdRoot.get(), NOD_PARTITION_KIND_DATA, nullptr, &partitionRaw);
405417
if (partitionResult != NOD_RESULT_OK || partitionRaw == nullptr) {
418+
const char* nodError = nod_error_message();
419+
m_lastError = fmt::format("nod_disc_open_partition_kind(data) failed ({}){}", int(partitionResult),
420+
nodError != nullptr && nodError[0] != '\0' ? fmt::format(": {}", nodError) : "");
421+
spdlog::error("{} (path: '{}')", m_lastError, pathStr);
406422
Shutdown();
407423
return false;
408424
}
409425
m_DataPartition = NodHandleUnique(partitionRaw, nod_free);
410426

411-
if (!BuildFileEntries() || !LoadDolBuf()) {
427+
if (!BuildFileEntries()) {
428+
m_lastError = "Failed to read disc file-system table";
429+
spdlog::error("{} (path: '{}')", m_lastError, pathStr);
430+
Shutdown();
431+
return false;
432+
}
433+
if (!LoadDolBuf()) {
434+
m_lastError = "Failed to load raw DOL data from disc";
435+
spdlog::error("{} (path: '{}')", m_lastError, pathStr);
412436
Shutdown();
413437
return false;
414438
}

Runtime/CDvdFile.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class CDvdFile {
4646

4747
static std::vector<std::shared_ptr<IDvdRequest>> m_RequestQueue;
4848
static std::string m_rootDirectory;
49+
static std::string m_lastError;
4950
static std::unique_ptr<u8[]> m_dolBuf;
5051
static size_t m_dolBufLen;
5152

@@ -61,6 +62,7 @@ class CDvdFile {
6162

6263
public:
6364
static bool Initialize(const std::string_view& path);
65+
static std::string_view GetLastError() { return m_lastError; }
6466
static SDiscInfo DiscInfo();
6567
static void SetRootDirectory(const std::string_view& rootDir);
6668
static void Shutdown();

Runtime/CMain.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <string_view>
33
#include <numeric>
44
#include <iostream>
5+
#include <vector>
56

67
#include "ImGuiEngine.hpp"
78
#include "Runtime/Graphics/CGraphics.hpp"
@@ -32,7 +33,11 @@
3233
#include <aurora/event.h>
3334
#include <aurora/main.h>
3435
#include <dolphin/vi.h>
36+
#include <SDL3/SDL_log.h>
3537
#include <SDL3/SDL_messagebox.h>
38+
#if defined(ANDROID)
39+
#include <spdlog/sinks/android_sink.h>
40+
#endif
3641
#include "Runtime/Graphics/CTexture.hpp"
3742

3843
using namespace std::literals;
@@ -202,10 +207,10 @@ struct Application {
202207
#if TARGET_OS_IOS || TARGET_OS_TV
203208
m_deferredProject = std::string{m_fileMgr.getStoreRoot()} + "game.iso";
204209
#else
205-
bool inArg = false;
206210
for (int i = 1; i < m_argc; ++i) {
207211
std::string arg = m_argv[i];
208-
if (m_deferredProject.empty() && !arg.starts_with('-') && !arg.starts_with('+') && CBasics::IsDir(arg.c_str()))
212+
if (m_deferredProject.empty() && !arg.starts_with('-') && !arg.starts_with('+') &&
213+
(CBasics::IsDir(arg.c_str()) || CBasics::IsFile(arg.c_str())))
209214
m_deferredProject = arg;
210215
else if (arg == "--no-sound") {
211216
// m_voiceEngine->setVolume(0.f);
@@ -261,8 +266,17 @@ struct Application {
261266
m_projectInitialized = true;
262267
m_cvarCommons.m_lastDiscPath->fromLiteral(m_deferredProject);
263268
} else {
264-
spdlog::error("Failed to open disc image '{}'", m_deferredProject);
265-
m_imGuiConsole.m_errorString = fmt::format("Failed to open disc image '{}'", m_deferredProject);
269+
const std::string_view dvdErr = CDvdFile::GetLastError();
270+
if (dvdErr.empty()) {
271+
spdlog::error("Failed to open disc image '{}'", m_deferredProject);
272+
m_imGuiConsole.m_errorString = fmt::format("Failed to open disc image '{}'", m_deferredProject);
273+
} else {
274+
spdlog::error("Failed to open disc image '{}': {}", m_deferredProject, dvdErr);
275+
m_imGuiConsole.m_errorString = fmt::format("Failed to open disc image '{}': {}", m_deferredProject, dvdErr);
276+
}
277+
if (m_deferredProject.starts_with("content://")) {
278+
m_cvarCommons.m_lastDiscPath->fromLiteral(""sv);
279+
}
266280
}
267281
m_deferredProject.clear();
268282
}
@@ -437,6 +451,20 @@ static void SetupBasics() {
437451
exit(1);
438452
}
439453

454+
#if defined(ANDROID)
455+
{
456+
std::vector<spdlog::sink_ptr> sinks;
457+
if (auto defaultLogger = spdlog::default_logger(); defaultLogger != nullptr) {
458+
sinks = defaultLogger->sinks();
459+
}
460+
sinks.emplace_back(std::make_shared<spdlog::sinks::android_sink_mt>("Metaforce"));
461+
auto logger = std::make_shared<spdlog::logger>("metaforce-android", sinks.begin(), sinks.end());
462+
logger->set_level(spdlog::level::trace);
463+
logger->flush_on(spdlog::level::warn);
464+
spdlog::set_default_logger(std::move(logger));
465+
}
466+
#endif
467+
440468
#if SENTRY_ENABLED
441469
std::string cacheDir{metaforce::FileStoreManager::instance()->getStoreRoot()};
442470
logvisor::RegisterSentry("metaforce", METAFORCE_WC_DESCRIBE, cacheDir.c_str());

Runtime/CMakeLists.txt

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,17 +214,32 @@ if (WIN32)
214214
endif ()
215215
elseif (APPLE)
216216
# nothing
217-
elseif (UNIX)
217+
elseif (UNIX AND NOT ANDROID)
218218
set(PLAT_LIBS rt)
219219
endif ()
220220

221-
add_executable(metaforce WIN32 CMain.cpp ${PLAT_SRCS}
222-
ImGuiConsole.hpp ImGuiConsole.cpp
223-
ImGuiControllerConfig.hpp ImGuiControllerConfig.cpp
224-
ImGuiEntitySupport.hpp ImGuiEntitySupport.cpp)
221+
if (ANDROID)
222+
add_library(metaforce SHARED CMain.cpp ${PLAT_SRCS}
223+
ImGuiConsole.hpp ImGuiConsole.cpp
224+
ImGuiControllerConfig.hpp ImGuiControllerConfig.cpp
225+
ImGuiEntitySupport.hpp ImGuiEntitySupport.cpp)
226+
set_target_properties(metaforce PROPERTIES
227+
OUTPUT_NAME main
228+
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Binaries)
229+
else ()
230+
add_executable(metaforce WIN32 CMain.cpp ${PLAT_SRCS}
231+
ImGuiConsole.hpp ImGuiConsole.cpp
232+
ImGuiControllerConfig.hpp ImGuiControllerConfig.cpp
233+
ImGuiEntitySupport.hpp ImGuiEntitySupport.cpp)
234+
endif ()
225235
# RUNTIME_LIBRARIES repeated here for link ordering
226236
target_link_libraries(metaforce PUBLIC RuntimeCommon RuntimeCommonB ${RUNTIME_LIBRARIES} ${PLAT_LIBS} aurora::main)
227237
target_compile_definitions(metaforce PUBLIC "-DMETAFORCE_TARGET_BYTE_ORDER=__BYTE_ORDER__")
238+
if (ANDROID)
239+
# SDLActivity loads SDL_main via dlsym on Android. Since aurora::main is a static
240+
# archive, force an undefined reference so the linker keeps the SDL_main object.
241+
target_link_options(metaforce PRIVATE "-Wl,-u,SDL_main")
242+
endif ()
228243
if (WIN32)
229244
target_link_options(metaforce PRIVATE /ENTRY:wWinMainCRTStartup)
230245
endif ()

Runtime/Graphics/CGraphics.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -513,15 +513,15 @@ void CGraphics::FlushProjection() {
513513
float left = mProj.GetLeft();
514514
float top = mProj.GetTop();
515515
float bottom = mProj.GetBottom();
516-
float near = mProj.GetNear();
517-
float far = mProj.GetFar();
516+
float nearPlane = mProj.GetNear();
517+
float farPlane = mProj.GetFar();
518518
if (mProj.IsPerspective()) {
519519
Mtx44 mtx;
520-
MTXFrustum(mtx, top, bottom, left, right, near, far);
520+
MTXFrustum(mtx, top, bottom, left, right, nearPlane, farPlane);
521521
GXSetProjection(mtx, GX_PERSPECTIVE);
522522
} else {
523523
Mtx44 mtx;
524-
MTXOrtho(mtx, top, bottom, left, right, near, far);
524+
MTXOrtho(mtx, top, bottom, left, right, nearPlane, farPlane);
525525
GXSetProjection(mtx, GX_ORTHOGRAPHIC);
526526
}
527527
}
@@ -638,9 +638,9 @@ void CGraphics::SetScissor(int left, int bottom, int width, int height) {
638638
GXSetScissor(left, mRenderModeObj.efbHeight - (bottom + height), width, height);
639639
}
640640

641-
void CGraphics::SetDepthRange(float near, float far) {
642-
mDepthNear = near;
643-
mDepthFar = far;
641+
void CGraphics::SetDepthRange(float nearPlane, float farPlane) {
642+
mDepthNear = nearPlane;
643+
mDepthFar = farPlane;
644644
GXSetViewport(static_cast<float>(mViewport.mLeft), static_cast<float>(mViewport.mTop),
645645
static_cast<float>(mViewport.mWidth), static_cast<float>(mViewport.mHeight), mDepthNear, mDepthFar);
646646
}

Runtime/Graphics/CGraphics.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,14 @@ class CGraphics {
191191

192192
class CProjectionState {
193193
public:
194-
CProjectionState(bool persp, float left, float right, float top, float bottom, float near, float far)
195-
: x0_persp(persp), x4_left(left), x8_right(right), xc_top(top), x10_bottom(bottom), x14_near(near), x18_far(far) {}
194+
CProjectionState(bool persp, float left, float right, float top, float bottom, float nearPlane, float farPlane)
195+
: x0_persp(persp)
196+
, x4_left(left)
197+
, x8_right(right)
198+
, xc_top(top)
199+
, x10_bottom(bottom)
200+
, x14_near(nearPlane)
201+
, x18_far(farPlane) {}
196202

197203
bool IsPerspective() const { return x0_persp; }
198204
float GetLeft() const { return x4_left; }
@@ -359,7 +365,7 @@ class CGraphics {
359365
static void SetViewportResolution(const zeus::CVector2i& res);
360366
static void SetViewport(int leftOff, int bottomOff, int width, int height);
361367
static void SetScissor(int leftOff, int bottomOff, int width, int height);
362-
static void SetDepthRange(float near, float far);
368+
static void SetDepthRange(float nearPlane, float farPlane);
363369
static void SetIdentityViewPointMatrix();
364370
static void SetIdentityModelMatrix();
365371
static void ClearBackAndDepthBuffers();

Runtime/ImGuiConsole.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,10 @@ void ImGuiConsole::ShowAboutWindow(bool preLaunch) {
729729
}
730730
if (m_errorString) {
731731
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.77f, 0.12f, 0.23f, 1.f});
732-
ImGuiTextCenter(*m_errorString);
732+
ImGui::NewLine();
733+
ImGui::PushTextWrapPos(0.f);
734+
ImGuiStringViewText(*m_errorString);
735+
ImGui::PopTextWrapPos();
733736
ImGui::PopStyleColor();
734737
ImGui::Dummy(padding);
735738
}

android/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.gradle/
2+
build/
3+
app/build/
4+
local.properties
5+
app/src/main/jniLibs/*/*.so

0 commit comments

Comments
 (0)