mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-01-12 18:51:15 +00:00
* isleapp: Show error dialog if NOCD.si fails to load * isleapp: Return bool in IsleApp::Tick foxtacles recommended me to return a boolean for if tick failed instead of just setting `g_closed` and setting `g_closeResult` to set the return value of the program exit. I also think this is the better solution. since a similar solution was used in LoadConfig. * isleapp: Better error logs & return false in places foxtacles recommended to use SDL's logging api to show detailed errors and using false in two places where it should exit because of failure. This commit adds these two things. * isleapp: Add missing error dialog when ISLE.si fails * isleapp: Add missing error dialog when ISLE.si fails * isleapp: Remove weird duplicate message box * Move error dialog to loop --------- Co-authored-by: Christian Semmler <mail@csemmler.com>
842 lines
20 KiB
C++
842 lines
20 KiB
C++
#include "isleapp.h"
|
|
|
|
#include "3dmanager/lego3dmanager.h"
|
|
#include "decomp.h"
|
|
#include "legoanimationmanager.h"
|
|
#include "legobuildingmanager.h"
|
|
#include "legogamestate.h"
|
|
#include "legoinputmanager.h"
|
|
#include "legomain.h"
|
|
#include "legomodelpresenter.h"
|
|
#include "legopartpresenter.h"
|
|
#include "legoutils.h"
|
|
#include "legovideomanager.h"
|
|
#include "legoworldpresenter.h"
|
|
#include "misc.h"
|
|
#include "mxbackgroundaudiomanager.h"
|
|
#include "mxdirectx/mxdirect3d.h"
|
|
#include "mxdsaction.h"
|
|
#include "mxmisc.h"
|
|
#include "mxomnicreateflags.h"
|
|
#include "mxomnicreateparam.h"
|
|
#include "mxstreamer.h"
|
|
#include "mxticklemanager.h"
|
|
#include "mxtimer.h"
|
|
#include "mxtransitionmanager.h"
|
|
#include "mxutilities.h"
|
|
#include "mxvariabletable.h"
|
|
#include "res/isle_bmp.h"
|
|
#include "res/resource.h"
|
|
#include "roi/legoroi.h"
|
|
#include "viewmanager/viewmanager.h"
|
|
|
|
#include <SDL3/SDL_init.h>
|
|
|
|
#define SDL_MAIN_USE_CALLBACKS
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_main.h>
|
|
#include <errno.h>
|
|
#include <iniparser.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
|
|
|
|
// GLOBAL: ISLE 0x410030
|
|
IsleApp* g_isle = NULL;
|
|
|
|
// GLOBAL: ISLE 0x410034
|
|
MxU8 g_mousedown = FALSE;
|
|
|
|
// GLOBAL: ISLE 0x410038
|
|
MxU8 g_mousemoved = FALSE;
|
|
|
|
// GLOBAL: ISLE 0x41003c
|
|
MxS32 g_closed = FALSE;
|
|
|
|
// GLOBAL: ISLE 0x410050
|
|
MxS32 g_rmDisabled = FALSE;
|
|
|
|
// GLOBAL: ISLE 0x410054
|
|
MxS32 g_waitingForTargetDepth = TRUE;
|
|
|
|
// GLOBAL: ISLE 0x410058
|
|
MxS32 g_targetWidth = 640;
|
|
|
|
// GLOBAL: ISLE 0x41005c
|
|
MxS32 g_targetHeight = 480;
|
|
|
|
// GLOBAL: ISLE 0x410060
|
|
MxS32 g_targetDepth = 16;
|
|
|
|
// GLOBAL: ISLE 0x410064
|
|
MxS32 g_reqEnableRMDevice = FALSE;
|
|
|
|
// STRING: ISLE 0x4101dc
|
|
#define WINDOW_TITLE "LEGO®"
|
|
|
|
SDL_Window* window;
|
|
|
|
// FUNCTION: ISLE 0x401000
|
|
IsleApp::IsleApp()
|
|
{
|
|
m_hdPath = NULL;
|
|
m_cdPath = NULL;
|
|
m_deviceId = NULL;
|
|
m_savePath = NULL;
|
|
m_fullScreen = TRUE;
|
|
m_flipSurfaces = FALSE;
|
|
m_backBuffersInVram = TRUE;
|
|
m_using8bit = FALSE;
|
|
m_using16bit = TRUE;
|
|
m_unk0x24 = 0;
|
|
m_drawCursor = FALSE;
|
|
m_use3dSound = TRUE;
|
|
m_useMusic = TRUE;
|
|
m_useJoystick = FALSE;
|
|
m_joystickIndex = 0;
|
|
m_wideViewAngle = TRUE;
|
|
m_islandQuality = 1;
|
|
m_islandTexture = 1;
|
|
m_gameStarted = FALSE;
|
|
m_frameDelta = 10;
|
|
m_windowActive = TRUE;
|
|
|
|
#ifdef COMPAT_MODE
|
|
{
|
|
MxRect32 r(0, 0, 639, 479);
|
|
MxVideoParamFlags flags;
|
|
m_videoParam = MxVideoParam(r, NULL, 1, flags);
|
|
}
|
|
#else
|
|
m_videoParam = MxVideoParam(MxRect32(0, 0, 639, 479), NULL, 1, MxVideoParamFlags());
|
|
#endif
|
|
m_videoParam.Flags().Set16Bit(MxDirectDraw::GetPrimaryBitDepth() == 16);
|
|
|
|
m_windowHandle = NULL;
|
|
m_cursorArrow = NULL;
|
|
m_cursorBusy = NULL;
|
|
m_cursorNo = NULL;
|
|
m_cursorCurrent = NULL;
|
|
|
|
LegoOmni::CreateInstance();
|
|
|
|
m_iniPath = NULL;
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x4011a0
|
|
IsleApp::~IsleApp()
|
|
{
|
|
if (LegoOmni::GetInstance()) {
|
|
Close();
|
|
MxOmni::DestroyInstance();
|
|
}
|
|
|
|
if (m_hdPath) {
|
|
delete[] m_hdPath;
|
|
}
|
|
|
|
if (m_cdPath) {
|
|
delete[] m_cdPath;
|
|
}
|
|
|
|
if (m_deviceId) {
|
|
delete[] m_deviceId;
|
|
}
|
|
|
|
if (m_savePath) {
|
|
delete[] m_savePath;
|
|
}
|
|
|
|
if (m_mediaPath) {
|
|
delete[] m_mediaPath;
|
|
}
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x401260
|
|
void IsleApp::Close()
|
|
{
|
|
MxDSAction ds;
|
|
ds.SetUnknown24(-2);
|
|
|
|
if (Lego()) {
|
|
GameState()->Save(0);
|
|
if (InputManager()) {
|
|
InputManager()->QueueEvent(c_notificationKeyPress, 0, 0, 0, SDLK_SPACE);
|
|
}
|
|
|
|
VideoManager()->Get3DManager()->GetLego3DView()->GetViewManager()->RemoveAll(NULL);
|
|
|
|
Lego()->RemoveWorld(ds.GetAtomId(), ds.GetObjectId());
|
|
Lego()->DeleteObject(ds);
|
|
TransitionManager()->SetWaitIndicator(NULL);
|
|
Lego()->Resume();
|
|
|
|
while (Streamer()->Close(NULL) == SUCCESS) {
|
|
}
|
|
|
|
while (Lego() && !Lego()->DoesEntityExist(ds)) {
|
|
Timer()->GetRealTime();
|
|
TickleManager()->Tickle();
|
|
}
|
|
}
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x4013b0
|
|
MxS32 IsleApp::SetupLegoOmni()
|
|
{
|
|
MxS32 result = FALSE;
|
|
|
|
#ifdef COMPAT_MODE
|
|
MxS32 failure;
|
|
{
|
|
MxOmniCreateParam param(m_mediaPath, m_windowHandle, m_videoParam, MxOmniCreateFlags());
|
|
failure = Lego()->Create(param) == FAILURE;
|
|
}
|
|
#else
|
|
MxS32 failure =
|
|
Lego()->Create(MxOmniCreateParam(m_mediaPath, m_windowHandle, m_videoParam, MxOmniCreateFlags())) == FAILURE;
|
|
#endif
|
|
|
|
if (!failure) {
|
|
VariableTable()->SetVariable("ACTOR_01", "");
|
|
TickleManager()->SetClientTickleInterval(VideoManager(), 10);
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x401560
|
|
void IsleApp::SetupVideoFlags(
|
|
MxS32 fullScreen,
|
|
MxS32 flipSurfaces,
|
|
MxS32 backBuffers,
|
|
MxS32 using8bit,
|
|
MxS32 using16bit,
|
|
MxS32 param_6,
|
|
MxS32 param_7,
|
|
MxS32 wideViewAngle,
|
|
char* deviceId
|
|
)
|
|
{
|
|
m_videoParam.Flags().SetFullScreen(fullScreen);
|
|
m_videoParam.Flags().SetFlipSurfaces(flipSurfaces);
|
|
m_videoParam.Flags().SetBackBuffers(!backBuffers);
|
|
m_videoParam.Flags().SetLacksLightSupport(!param_6);
|
|
m_videoParam.Flags().SetF1bit7(param_7);
|
|
m_videoParam.Flags().SetWideViewAngle(wideViewAngle);
|
|
m_videoParam.Flags().SetF2bit1(1);
|
|
m_videoParam.SetDeviceName(deviceId);
|
|
if (using8bit) {
|
|
m_videoParam.Flags().Set16Bit(0);
|
|
}
|
|
if (using16bit) {
|
|
m_videoParam.Flags().Set16Bit(1);
|
|
}
|
|
}
|
|
|
|
SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
|
|
{
|
|
*appstate = NULL;
|
|
|
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK)) {
|
|
char buffer[256];
|
|
SDL_snprintf(
|
|
buffer,
|
|
sizeof(buffer),
|
|
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.\nSDL error: %s",
|
|
SDL_GetError()
|
|
);
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", buffer, NULL);
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
// [library:window]
|
|
// Original game checks for an existing instance here.
|
|
// We don't really need that.
|
|
|
|
// Create global app instance
|
|
g_isle = new IsleApp();
|
|
|
|
if (g_isle->ParseArguments(argc, argv) != SUCCESS) {
|
|
SDL_ShowSimpleMessageBox(
|
|
SDL_MESSAGEBOX_ERROR,
|
|
"LEGO® Island Error",
|
|
"\"LEGO® Island\" failed to start. Invalid CLI arguments.",
|
|
window
|
|
);
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
// Create window
|
|
if (g_isle->SetupWindow() != SUCCESS) {
|
|
SDL_ShowSimpleMessageBox(
|
|
SDL_MESSAGEBOX_ERROR,
|
|
"LEGO® Island Error",
|
|
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.",
|
|
window
|
|
);
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
// Get reference to window
|
|
*appstate = g_isle->GetWindowHandle();
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
SDL_AppResult SDL_AppIterate(void* appstate)
|
|
{
|
|
if (g_closed) {
|
|
return SDL_APP_SUCCESS;
|
|
}
|
|
|
|
if (!g_isle->Tick()) {
|
|
SDL_ShowSimpleMessageBox(
|
|
SDL_MESSAGEBOX_ERROR,
|
|
"LEGO® Island Error",
|
|
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again."
|
|
"\nFailed to initialize; see logs for details",
|
|
NULL
|
|
);
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
if (!g_closed) {
|
|
if (g_reqEnableRMDevice) {
|
|
g_reqEnableRMDevice = FALSE;
|
|
VideoManager()->EnableRMDevice();
|
|
g_rmDisabled = FALSE;
|
|
Lego()->Resume();
|
|
}
|
|
|
|
if (g_closed) {
|
|
return SDL_APP_SUCCESS;
|
|
}
|
|
|
|
if (g_mousedown && g_mousemoved && g_isle) {
|
|
if (!g_isle->Tick()) {
|
|
SDL_ShowSimpleMessageBox(
|
|
SDL_MESSAGEBOX_ERROR,
|
|
"LEGO® Island Error",
|
|
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again."
|
|
"\nFailed to initialize; see logs for details",
|
|
NULL
|
|
);
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (g_mousemoved) {
|
|
g_mousemoved = FALSE;
|
|
}
|
|
}
|
|
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
|
|
{
|
|
if (!g_isle) {
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
// [library:window]
|
|
// Remaining functionality to be implemented:
|
|
// Full screen - crashes when minimizing/maximizing, but this will probably be fixed once DirectDraw is replaced
|
|
// WM_TIMER - use SDL_Timer functionality instead
|
|
|
|
switch (event->type) {
|
|
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
|
g_isle->SetWindowActive(TRUE);
|
|
Lego()->Resume();
|
|
break;
|
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
|
g_isle->SetWindowActive(FALSE);
|
|
Lego()->Pause();
|
|
break;
|
|
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
|
if (!g_closed) {
|
|
delete g_isle;
|
|
g_isle = NULL;
|
|
g_closed = TRUE;
|
|
}
|
|
break;
|
|
case SDL_EVENT_KEY_DOWN: {
|
|
if (event->key.repeat) {
|
|
break;
|
|
}
|
|
|
|
SDL_Keycode keyCode = event->key.key;
|
|
if (InputManager()) {
|
|
InputManager()->QueueEvent(c_notificationKeyPress, keyCode, 0, 0, keyCode);
|
|
}
|
|
break;
|
|
}
|
|
case SDL_EVENT_MOUSE_MOTION:
|
|
g_mousemoved = TRUE;
|
|
|
|
if (InputManager()) {
|
|
InputManager()->QueueEvent(
|
|
c_notificationMouseMove,
|
|
IsleApp::MapMouseButtonFlagsToModifier(event->motion.state),
|
|
event->motion.x,
|
|
event->motion.y,
|
|
0
|
|
);
|
|
}
|
|
|
|
if (g_isle->GetDrawCursor()) {
|
|
VideoManager()->MoveCursor(Min((MxS32) event->motion.x, 639), Min((MxS32) event->motion.y, 479));
|
|
}
|
|
break;
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
g_mousedown = TRUE;
|
|
|
|
if (InputManager()) {
|
|
InputManager()->QueueEvent(
|
|
c_notificationButtonDown,
|
|
IsleApp::MapMouseButtonFlagsToModifier(SDL_GetMouseState(NULL, NULL)),
|
|
event->button.x,
|
|
event->button.y,
|
|
0
|
|
);
|
|
}
|
|
break;
|
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
|
g_mousedown = FALSE;
|
|
|
|
if (InputManager()) {
|
|
InputManager()->QueueEvent(
|
|
c_notificationButtonUp,
|
|
IsleApp::MapMouseButtonFlagsToModifier(SDL_GetMouseState(NULL, NULL)),
|
|
event->button.x,
|
|
event->button.y,
|
|
0
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (event->user.type == g_legoSdlEvents.m_windowsMessage) {
|
|
switch (event->user.code) {
|
|
case WM_ISLE_SETCURSOR:
|
|
g_isle->SetupCursor((Cursor) (uintptr_t) event->user.data1);
|
|
break;
|
|
case WM_QUIT:
|
|
return SDL_APP_SUCCESS;
|
|
break;
|
|
case WM_TIMER:
|
|
if (InputManager()) {
|
|
InputManager()->QueueEvent(c_notificationTimer, (MxU8) (uintptr_t) event->user.data1, 0, 0, 0);
|
|
}
|
|
break;
|
|
default:
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unknown SDL Windows message: 0x%" SDL_PRIx32, event->user.code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
void SDL_AppQuit(void* appstate, SDL_AppResult result)
|
|
{
|
|
if (appstate != NULL) {
|
|
SDL_DestroyWindow((SDL_Window*) appstate);
|
|
}
|
|
|
|
SDL_Quit();
|
|
}
|
|
|
|
MxU8 IsleApp::MapMouseButtonFlagsToModifier(SDL_MouseButtonFlags p_flags)
|
|
{
|
|
// [library:window]
|
|
// Map button states to Windows button states (LegoEventNotificationParam)
|
|
// Not mapping mod keys SHIFT and CTRL since they are not used by the game.
|
|
|
|
MxU8 modifier = 0;
|
|
if (p_flags & SDL_BUTTON_LMASK) {
|
|
modifier |= LegoEventNotificationParam::c_lButtonState;
|
|
}
|
|
if (p_flags & SDL_BUTTON_RMASK) {
|
|
modifier |= LegoEventNotificationParam::c_rButtonState;
|
|
}
|
|
|
|
return modifier;
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x4023e0
|
|
MxResult IsleApp::SetupWindow()
|
|
{
|
|
if (!LoadConfig()) {
|
|
return FAILURE;
|
|
}
|
|
|
|
SetupVideoFlags(
|
|
m_fullScreen,
|
|
m_flipSurfaces,
|
|
m_backBuffersInVram,
|
|
m_using8bit,
|
|
m_using16bit,
|
|
m_unk0x24,
|
|
FALSE,
|
|
m_wideViewAngle,
|
|
m_deviceId
|
|
);
|
|
|
|
MxOmni::SetSound3D(m_use3dSound);
|
|
|
|
srand(time(NULL));
|
|
|
|
// [library:window] Use original game cursors in the resources instead?
|
|
m_cursorCurrent = m_cursorArrow = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT);
|
|
m_cursorBusy = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
|
|
m_cursorNo = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED);
|
|
SDL_SetCursor(m_cursorCurrent);
|
|
|
|
SDL_PropertiesID props = SDL_CreateProperties();
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, g_targetWidth);
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, g_targetHeight);
|
|
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen);
|
|
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE);
|
|
|
|
window = SDL_CreateWindowWithProperties(props);
|
|
#ifndef MINIWIN
|
|
m_windowHandle =
|
|
(HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
|
|
#else
|
|
m_windowHandle = window;
|
|
#endif
|
|
|
|
SDL_DestroyProperties(props);
|
|
|
|
if (!m_windowHandle) {
|
|
return FAILURE;
|
|
}
|
|
|
|
SDL_IOStream* icon_stream = SDL_IOFromMem(isle_bmp, isle_bmp_len);
|
|
|
|
if (icon_stream) {
|
|
SDL_Surface* icon = SDL_LoadBMP_IO(icon_stream, true);
|
|
|
|
if (icon) {
|
|
SDL_SetWindowIcon(window, icon);
|
|
SDL_DestroySurface(icon);
|
|
}
|
|
else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load icon: %s", SDL_GetError());
|
|
}
|
|
}
|
|
else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open SDL_IOStream for icon: %s", SDL_GetError());
|
|
}
|
|
|
|
if (!SetupLegoOmni()) {
|
|
return FAILURE;
|
|
}
|
|
|
|
GameState()->SetSavePath(m_savePath);
|
|
GameState()->SerializePlayersInfo(LegoStorage::c_read);
|
|
GameState()->SerializeScoreHistory(LegoStorage::c_read);
|
|
|
|
MxS32 iVar10;
|
|
switch (m_islandQuality) {
|
|
case 0:
|
|
iVar10 = 1;
|
|
break;
|
|
case 1:
|
|
iVar10 = 2;
|
|
break;
|
|
default:
|
|
iVar10 = 100;
|
|
}
|
|
|
|
MxS32 uVar1 = (m_islandTexture == 0);
|
|
LegoModelPresenter::configureLegoModelPresenter(uVar1);
|
|
LegoPartPresenter::configureLegoPartPresenter(uVar1, iVar10);
|
|
LegoWorldPresenter::configureLegoWorldPresenter(m_islandQuality);
|
|
LegoBuildingManager::configureLegoBuildingManager(m_islandQuality);
|
|
LegoROI::configureLegoROI(iVar10);
|
|
LegoAnimationManager::configureLegoAnimationManager(m_islandQuality);
|
|
if (LegoOmni::GetInstance()) {
|
|
if (LegoOmni::GetInstance()->GetInputManager()) {
|
|
LegoOmni::GetInstance()->GetInputManager()->SetUseJoystick(m_useJoystick);
|
|
LegoOmni::GetInstance()->GetInputManager()->SetJoystickIndex(m_joystickIndex);
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x4028d0
|
|
bool IsleApp::LoadConfig()
|
|
{
|
|
char* prefPath = SDL_GetPrefPath("isledecomp", "isle");
|
|
char* iniConfig;
|
|
if (m_iniPath) {
|
|
iniConfig = new char[strlen(m_iniPath) + 1];
|
|
strcpy(iniConfig, m_iniPath);
|
|
}
|
|
else if (prefPath) {
|
|
iniConfig = new char[strlen(prefPath) + strlen("isle.ini") + 1]();
|
|
strcat(iniConfig, prefPath);
|
|
strcat(iniConfig, "isle.ini");
|
|
}
|
|
else {
|
|
iniConfig = new char[strlen("isle.ini") + 1];
|
|
strcpy(iniConfig, "isle.ini");
|
|
}
|
|
SDL_Log("Reading configuration from \"%s\"", iniConfig);
|
|
|
|
dictionary* dict = iniparser_load(iniConfig);
|
|
|
|
// [library:config]
|
|
// Load sane defaults if dictionary failed to load
|
|
if (!dict) {
|
|
if (m_iniPath) {
|
|
SDL_Log("Invalid config path '%s'", m_iniPath);
|
|
return false;
|
|
}
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading sane defaults");
|
|
FILE* iniFP = fopen(iniConfig, "wb");
|
|
|
|
if (!iniFP) {
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_APPLICATION,
|
|
"Failed to write config at '%s': %s",
|
|
iniConfig,
|
|
strerror(errno)
|
|
);
|
|
return false;
|
|
}
|
|
|
|
dict = iniparser_load(iniConfig);
|
|
iniparser_set(dict, "isle", NULL);
|
|
|
|
iniparser_set(dict, "isle:diskpath", SDL_GetBasePath());
|
|
iniparser_set(dict, "isle:cdpath", MxOmni::GetCD());
|
|
iniparser_set(dict, "isle:mediapath", SDL_GetBasePath());
|
|
iniparser_set(dict, "isle:savepath", prefPath);
|
|
|
|
iniparser_set(dict, "isle:Flip Surfaces", m_flipSurfaces ? "true" : "false");
|
|
iniparser_set(dict, "isle:Full Screen", m_fullScreen ? "true" : "false");
|
|
iniparser_set(dict, "isle:Wide View Angle", m_wideViewAngle ? "true" : "false");
|
|
|
|
iniparser_set(dict, "isle:3DSound", m_use3dSound ? "true" : "false");
|
|
iniparser_set(dict, "isle:Music", m_useMusic ? "true" : "false");
|
|
|
|
iniparser_set(dict, "isle:UseJoystick", m_useJoystick ? "true" : "false");
|
|
iniparser_set(dict, "isle:JoystickIndex", m_joystickIndex ? "true" : "false");
|
|
iniparser_set(dict, "isle:Draw Cursor", m_drawCursor ? "true" : "false");
|
|
|
|
iniparser_set(dict, "isle:Back Buffers in Video RAM", "-1");
|
|
|
|
iniparser_set(dict, "isle:Island Quality", "1");
|
|
iniparser_set(dict, "isle:Island Texture", "1");
|
|
|
|
iniparser_dump_ini(dict, iniFP);
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig);
|
|
fclose(iniFP);
|
|
}
|
|
|
|
const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath());
|
|
m_hdPath = new char[strlen(hdPath) + 1];
|
|
strcpy(m_hdPath, hdPath);
|
|
MxOmni::SetHD(m_hdPath);
|
|
|
|
const char* cdPath = iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD());
|
|
m_cdPath = new char[strlen(cdPath) + 1];
|
|
strcpy(m_cdPath, cdPath);
|
|
MxOmni::SetCD(m_cdPath);
|
|
|
|
const char* mediaPath = iniparser_getstring(dict, "isle:mediapath", hdPath);
|
|
m_mediaPath = new char[strlen(mediaPath) + 1];
|
|
strcpy(m_mediaPath, mediaPath);
|
|
|
|
m_flipSurfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flipSurfaces);
|
|
m_fullScreen = iniparser_getboolean(dict, "isle:Full Screen", m_fullScreen);
|
|
m_wideViewAngle = iniparser_getboolean(dict, "isle:Wide View Angle", m_wideViewAngle);
|
|
m_use3dSound = iniparser_getboolean(dict, "isle:3DSound", m_use3dSound);
|
|
m_useMusic = iniparser_getboolean(dict, "isle:Music", m_useMusic);
|
|
m_useJoystick = iniparser_getboolean(dict, "isle:UseJoystick", m_useJoystick);
|
|
m_joystickIndex = iniparser_getint(dict, "isle:JoystickIndex", m_joystickIndex);
|
|
m_drawCursor = iniparser_getboolean(dict, "isle:Draw Cursor", m_drawCursor);
|
|
|
|
MxS32 backBuffersInVRAM = iniparser_getboolean(dict, "isle:Back Buffers in Video RAM", -1);
|
|
if (backBuffersInVRAM != -1) {
|
|
m_backBuffersInVram = !backBuffersInVRAM;
|
|
}
|
|
|
|
MxS32 bitDepth = iniparser_getint(dict, "isle:Display Bit Depth", -1);
|
|
if (bitDepth != -1) {
|
|
if (bitDepth == 8) {
|
|
m_using8bit = TRUE;
|
|
}
|
|
else if (bitDepth == 16) {
|
|
m_using16bit = TRUE;
|
|
}
|
|
}
|
|
|
|
m_islandQuality = iniparser_getint(dict, "isle:Island Quality", 1);
|
|
m_islandTexture = iniparser_getint(dict, "isle:Island Texture", 1);
|
|
|
|
const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL);
|
|
if (deviceId != NULL) {
|
|
m_deviceId = new char[strlen(deviceId) + 1];
|
|
strcpy(m_deviceId, deviceId);
|
|
}
|
|
|
|
// [library:config]
|
|
// The original game does not save any data if no savepath is given.
|
|
// Instead, we use SDLs prefPath as a default fallback and always save data.
|
|
const char* savePath = iniparser_getstring(dict, "isle:savepath", prefPath);
|
|
m_savePath = new char[strlen(savePath) + 1];
|
|
strcpy(m_savePath, savePath);
|
|
|
|
iniparser_freedict(dict);
|
|
delete[] iniConfig;
|
|
SDL_free(prefPath);
|
|
|
|
return true;
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x402c20
|
|
inline bool IsleApp::Tick()
|
|
{
|
|
// GLOBAL: ISLE 0x4101c0
|
|
static MxLong g_lastFrameTime = 0;
|
|
|
|
// GLOBAL: ISLE 0x4101bc
|
|
static MxS32 g_startupDelay = 200;
|
|
|
|
if (!m_windowActive) {
|
|
SDL_Delay(1);
|
|
return true;
|
|
}
|
|
|
|
if (!Lego()) {
|
|
return true;
|
|
}
|
|
if (!TickleManager()) {
|
|
return true;
|
|
}
|
|
if (!Timer()) {
|
|
return true;
|
|
}
|
|
|
|
MxLong currentTime = Timer()->GetRealTime();
|
|
if (currentTime < g_lastFrameTime) {
|
|
g_lastFrameTime = -m_frameDelta;
|
|
}
|
|
|
|
if (m_frameDelta + g_lastFrameTime >= currentTime) {
|
|
SDL_Delay(1);
|
|
return true;
|
|
}
|
|
|
|
if (!Lego()->IsPaused()) {
|
|
TickleManager()->Tickle();
|
|
}
|
|
g_lastFrameTime = currentTime;
|
|
|
|
if (g_startupDelay == 0) {
|
|
return true;
|
|
}
|
|
|
|
g_startupDelay--;
|
|
if (g_startupDelay != 0) {
|
|
return true;
|
|
}
|
|
|
|
LegoOmni::GetInstance()->CreateBackgroundAudio();
|
|
BackgroundAudioManager()->Enable(m_useMusic);
|
|
|
|
MxStreamController* stream = Streamer()->Open("\\lego\\scripts\\isle\\isle", MxStreamer::e_diskStream);
|
|
MxDSAction ds;
|
|
|
|
if (!stream) {
|
|
stream = Streamer()->Open("\\lego\\scripts\\nocd", MxStreamer::e_diskStream);
|
|
if (!stream) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open NOCD.si: Streamer failed to load");
|
|
return false;
|
|
}
|
|
|
|
ds.SetAtomId(stream->GetAtom());
|
|
ds.SetUnknown24(-1);
|
|
ds.SetObjectId(0);
|
|
VideoManager()->EnableFullScreenMovie(TRUE, TRUE);
|
|
|
|
if (Start(&ds) != SUCCESS) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open NOCD.si: Failed to start initial action");
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
ds.SetAtomId(stream->GetAtom());
|
|
ds.SetUnknown24(-1);
|
|
ds.SetObjectId(0);
|
|
if (Start(&ds) != SUCCESS) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open ISLE.si: Failed to start initial action");
|
|
return false;
|
|
}
|
|
m_gameStarted = TRUE;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// FUNCTION: ISLE 0x402e80
|
|
void IsleApp::SetupCursor(Cursor p_cursor)
|
|
{
|
|
switch (p_cursor) {
|
|
case e_cursorArrow:
|
|
m_cursorCurrent = m_cursorArrow;
|
|
break;
|
|
case e_cursorBusy:
|
|
m_cursorCurrent = m_cursorBusy;
|
|
break;
|
|
case e_cursorNo:
|
|
m_cursorCurrent = m_cursorNo;
|
|
break;
|
|
case e_cursorNone:
|
|
m_cursorCurrent = NULL;
|
|
case e_cursorUnused3:
|
|
case e_cursorUnused4:
|
|
case e_cursorUnused5:
|
|
case e_cursorUnused6:
|
|
case e_cursorUnused7:
|
|
case e_cursorUnused8:
|
|
case e_cursorUnused9:
|
|
case e_cursorUnused10:
|
|
break;
|
|
}
|
|
|
|
if (m_cursorCurrent != NULL) {
|
|
SDL_SetCursor(m_cursorCurrent);
|
|
SDL_ShowCursor();
|
|
}
|
|
else {
|
|
SDL_HideCursor();
|
|
}
|
|
}
|
|
|
|
MxResult IsleApp::ParseArguments(int argc, char** argv)
|
|
{
|
|
|
|
for (int i = 1, consumed; i < argc; i += consumed) {
|
|
consumed = -1;
|
|
|
|
if (strcmp(argv[i], "--ini") == 0 && i + 1 < argc) {
|
|
m_iniPath = argv[i + 1];
|
|
consumed = 2;
|
|
}
|
|
if (consumed <= 0) {
|
|
SDL_Log("Invalid argument(s): %s", argv[i]);
|
|
return FAILURE;
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|