diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp
index 66f13462..731aa6e1 100644
--- a/ISLE/isleapp.cpp
+++ b/ISLE/isleapp.cpp
@@ -232,12 +232,14 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
 	*appstate = NULL;
 
 	if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK)) {
-		SDL_ShowSimpleMessageBox(
-			SDL_MESSAGEBOX_ERROR,
-			"LEGO® Island Error",
-			"\"LEGO® Island\" failed to start.  Please quit all other applications and try again.",
-			NULL
+		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;
 	}
 
@@ -253,7 +255,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
 			SDL_MESSAGEBOX_ERROR,
 			"LEGO® Island Error",
 			"\"LEGO® Island\" failed to start.  Invalid CLI arguments.",
-			NULL
+			g_isle->GetWindowHandle()
 		);
 		return SDL_APP_FAILURE;
 	}
@@ -263,8 +265,8 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
 		SDL_ShowSimpleMessageBox(
 			SDL_MESSAGEBOX_ERROR,
 			"LEGO® Island Error",
-			"\"LEGO® Island\" failed to start.  Please quit all other applications and try again.",
-			NULL
+			"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.",
+			g_isle->GetWindowHandle()
 		);
 		return SDL_APP_FAILURE;
 	}
diff --git a/LEGO1/lego/legoomni/include/legotextureinfo.h b/LEGO1/lego/legoomni/include/legotextureinfo.h
index e7cd894b..c070bfec 100644
--- a/LEGO1/lego/legoomni/include/legotextureinfo.h
+++ b/LEGO1/lego/legoomni/include/legotextureinfo.h
@@ -19,7 +19,7 @@ public:
 	static BOOL SetGroupTexture(Tgl::Mesh* pMesh, LegoTextureInfo* p_textureInfo);
 	static BOOL GetGroupTexture(Tgl::Mesh* pMesh, LegoTextureInfo*& p_textureInfo);
 
-	LegoResult FUN_10066010(LegoU8* p_bits);
+	LegoResult FUN_10066010(const LegoU8* p_bits);
 
 	// private:
 	char* m_name;                   // 0x00
diff --git a/LEGO1/lego/legoomni/include/pizza.h b/LEGO1/lego/legoomni/include/pizza.h
index b08e5310..a3d554fe 100644
--- a/LEGO1/lego/legoomni/include/pizza.h
+++ b/LEGO1/lego/legoomni/include/pizza.h
@@ -6,6 +6,8 @@
 #include "isleactor.h"
 #include "legostate.h"
 
+#include <limits.h>
+
 class Act1State;
 class PizzeriaState;
 class SkateBoard;
diff --git a/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp b/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp
index 2379b2d1..b6095308 100644
--- a/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp
+++ b/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp
@@ -28,8 +28,8 @@
 #include "realtime/realtime.h"
 #include "viewmanager/viewmanager.h"
 
+#include <SDL3/SDL_filesystem.h>
 #include <SDL3/SDL_stdinc.h>
-#include <io.h>
 #include <stdio.h>
 #include <vec.h>
 
@@ -640,8 +640,9 @@ MxResult LegoAnimationManager::LoadWorldInfo(LegoOmni::World p_worldId)
 		}
 
 		strcat(path, filename);
+		SDL_PathInfo pathInfo;
 
-		if (_access(path, 4)) {
+		if (!SDL_GetPathInfo(path, &pathInfo) || pathInfo.type != SDL_PATHTYPE_FILE) {
 			sprintf(path, "%s", MxOmni::GetCD());
 
 			if (path[strlen(path) - 1] != '\\') {
@@ -650,7 +651,7 @@ MxResult LegoAnimationManager::LoadWorldInfo(LegoOmni::World p_worldId)
 
 			strcat(path, filename);
 
-			if (_access(path, 4)) {
+			if (!SDL_GetPathInfo(path, &pathInfo) || pathInfo.type != SDL_PATHTYPE_FILE) {
 				goto done;
 			}
 		}
diff --git a/LEGO1/lego/legoomni/src/common/legogamestate.cpp b/LEGO1/lego/legoomni/src/common/legogamestate.cpp
index 21e12a45..492835b4 100644
--- a/LEGO1/lego/legoomni/src/common/legogamestate.cpp
+++ b/LEGO1/lego/legoomni/src/common/legogamestate.cpp
@@ -61,6 +61,7 @@
 #include "sndanim_actions.h"
 #include "towtrack.h"
 
+#include <SDL3/SDL_filesystem.h>
 #include <SDL3/SDL_stdinc.h>
 #include <assert.h>
 #include <stdio.h>
@@ -557,7 +558,7 @@ MxResult LegoGameState::AddPlayer(Username& p_player)
 
 	if (m_playerCount == 9) {
 		GetFileSavePath(&from, 8);
-		DeleteFile(from.GetData());
+		SDL_RemovePath(from.GetData());
 		m_playerCount--;
 	}
 
@@ -565,7 +566,7 @@ MxResult LegoGameState::AddPlayer(Username& p_player)
 		m_players[i] = m_players[i - 1];
 		GetFileSavePath(&from, i - 1);
 		GetFileSavePath(&to, i);
-		MoveFile(from.GetData(), to.GetData());
+		SDL_RenamePath(from.GetData(), to.GetData());
 	}
 
 	m_playerCount++;
@@ -589,18 +590,18 @@ void LegoGameState::SwitchPlayer(MxS16 p_playerId)
 
 		Username selectedName(m_players[p_playerId]);
 
-		MoveFile(from.GetData(), temp.GetData());
+		SDL_RenamePath(from.GetData(), temp.GetData());
 
 		for (MxS16 i = p_playerId; i > 0; i--) {
 			m_players[i] = m_players[i - 1];
 			GetFileSavePath(&from, i - 1);
 			GetFileSavePath(&to, i);
-			MoveFile(from.GetData(), to.GetData());
+			SDL_RenamePath(from.GetData(), to.GetData());
 		}
 
 		m_players[0] = selectedName;
 		GetFileSavePath(&from, 0);
-		MoveFile(temp.GetData(), from.GetData());
+		SDL_RenamePath(temp.GetData(), from.GetData());
 	}
 
 	if (Load(0) != SUCCESS) {
diff --git a/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp b/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp
index bf71acf2..b587cccd 100644
--- a/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp
+++ b/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp
@@ -71,7 +71,7 @@ LegoTextureInfo* LegoTextureInfo::Create(const char* p_name, LegoTexture* p_text
 	desc.ddpfPixelFormat.dwRGBBitCount = 8;
 
 	MxS32 i;
-	LegoU8* bits;
+	const LegoU8* bits;
 	MxU8* surface;
 
 	if (pDirectDraw->CreateSurface(&desc, &textureInfo->m_surface, NULL) != DD_OK) {
@@ -107,9 +107,9 @@ LegoTextureInfo* LegoTextureInfo::Create(const char* p_name, LegoTexture* p_text
 	for (i = 0; i < sizeOfArray(entries); i++) {
 		if (i < image->GetCount()) {
 			entries[i].peFlags = 0;
-			entries[i].peRed = image->GetPaletteEntry(i).GetRed();
-			entries[i].peGreen = image->GetPaletteEntry(i).GetGreen();
-			entries[i].peBlue = image->GetPaletteEntry(i).GetBlue();
+			entries[i].peRed = image->GetPalette()->colors[i].r;
+			entries[i].peGreen = image->GetPalette()->colors[i].g;
+			entries[i].peBlue = image->GetPalette()->colors[i].b;
 		}
 		else {
 			entries[i].peFlags = 0x80;
@@ -186,7 +186,7 @@ BOOL LegoTextureInfo::GetGroupTexture(Tgl::Mesh* pMesh, LegoTextureInfo*& p_text
 }
 
 // FUNCTION: LEGO1 0x10066010
-LegoResult LegoTextureInfo::FUN_10066010(LegoU8* p_bits)
+LegoResult LegoTextureInfo::FUN_10066010(const LegoU8* p_bits)
 {
 	if (m_surface != NULL && m_texture != NULL) {
 		DDSURFACEDESC desc;
@@ -195,7 +195,7 @@ LegoResult LegoTextureInfo::FUN_10066010(LegoU8* p_bits)
 
 		if (m_surface->Lock(NULL, &desc, 0, NULL) == DD_OK) {
 			MxU8* surface = (MxU8*) desc.lpSurface;
-			LegoU8* bits = p_bits;
+			const LegoU8* bits = p_bits;
 
 			if (desc.dwWidth == desc.lPitch) {
 				memcpy(desc.lpSurface, p_bits, desc.dwWidth * desc.dwHeight);
diff --git a/LEGO1/lego/legoomni/src/common/legoutils.cpp b/LEGO1/lego/legoomni/src/common/legoutils.cpp
index 8c8b2cae..0f5f641c 100644
--- a/LEGO1/lego/legoomni/src/common/legoutils.cpp
+++ b/LEGO1/lego/legoomni/src/common/legoutils.cpp
@@ -31,8 +31,8 @@
 #include "scripts.h"
 
 #include <SDL3/SDL_events.h>
+#include <SDL3/SDL_process.h>
 #include <SDL3/SDL_stdinc.h>
-#include <process.h>
 #include <stdio.h>
 #include <string.h>
 #include <vec.h>
@@ -320,9 +320,10 @@ void InvokeAction(Extra::ActionType p_actionId, const MxAtomId& p_pAtom, MxS32 p
 		}
 
 		break;
-	case Extra::ActionType::e_run:
-		_spawnl(0, "\\lego\\sources\\main\\main.exe", "\\lego\\sources\\main\\main.exe", "/script", &p_pAtom, 0);
-		break;
+	case Extra::ActionType::e_run: {
+		const char* args[] = {"/lego/sources/main/main.exe", "/script", p_pAtom.GetInternal(), NULL};
+		SDL_Process* process = SDL_CreateProcess(args, false);
+	} break;
 	case Extra::ActionType::e_enable:
 		assert(p_streamId != DS_NOT_A_STREAM);
 		CheckIfEntityExists(TRUE, p_pAtom.GetInternal(), p_streamId);
@@ -729,7 +730,7 @@ void WriteDefaultTexture(LegoFile* p_file, const char* p_name)
 				}
 				else {
 					MxU8* surface = (MxU8*) desc.lpSurface;
-					LegoU8* bits = image->GetBits();
+					const LegoU8* bits = image->GetBits();
 
 					for (MxS32 i = 0; i < desc.dwHeight; i++) {
 						memcpy(surface, bits, desc.dwWidth);
@@ -753,12 +754,14 @@ void WriteDefaultTexture(LegoFile* p_file, const char* p_name)
 						paletteEntries[i].SetBlue(entries[i].peBlue);
 					}
 
-					image->SetCount(i);
+					SDL_Palette* newPalette = SDL_CreatePalette(i);
 
 					if (i > 0) {
-						// Note: this appears to be a bug. size should be i * sizeof(LegoPaletteEntry)
-						memcpy(image->GetPalette(), paletteEntries, i);
+						for (MxS32 j = 0; j < i; j++) {
+							image->SetPaletteEntry(j, paletteEntries[j]);
+						}
 					}
+					image->SetPalette(newPalette);
 
 					LegoTexture texture;
 					texture.SetImage(image);
diff --git a/LEGO1/lego/legoomni/src/main/legomain.cpp b/LEGO1/lego/legoomni/src/main/legomain.cpp
index f5d725be..0c4bd702 100644
--- a/LEGO1/lego/legoomni/src/main/legomain.cpp
+++ b/LEGO1/lego/legoomni/src/main/legomain.cpp
@@ -33,6 +33,7 @@
 #include "scripts.h"
 #include "viewmanager/viewmanager.h"
 
+#include <SDL3/SDL_log.h>
 #include <SDL3/SDL_stdinc.h>
 
 DECOMP_SIZE_ASSERT(LegoOmni, 0x140)
@@ -168,18 +169,22 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param)
 	p_param.CreateFlags().CreateTickleManager(FALSE);
 
 	if (!(m_tickleManager = new MxTickleManager())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create MxTickleManager");
 		goto done;
 	}
 
 	if (MxOmni::Create(p_param) != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create MxOmni");
 		goto done;
 	}
 
 	if (!(m_objectFactory = new LegoObjectFactory())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create LegoObjectFactory");
 		goto done;
 	}
 
 	if (!(m_soundManager = new LegoSoundManager()) || m_soundManager->Create(10, 0) != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create LegoSoundManager");
 		delete m_soundManager;
 		m_soundManager = NULL;
 		goto done;
@@ -187,6 +192,7 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param)
 
 	if (!(m_videoManager = new LegoVideoManager()) ||
 		m_videoManager->Create(p_param.GetVideoParam(), 100, 0) != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create LegoVideoManager");
 		delete m_videoManager;
 		m_videoManager = NULL;
 		goto done;
@@ -199,6 +205,7 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param)
 		NULL
 	);
 	if (!(m_inputManager = new LegoInputManager()) || m_inputManager->Create(hWnd) != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create LegoInputManager");
 		delete m_inputManager;
 		m_inputManager = NULL;
 		goto done;
@@ -218,27 +225,37 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param)
 
 	if (!m_viewLODListManager || !m_textureContainer || !m_worldList || !m_characterManager || !m_plantManager ||
 		!m_animationManager || !m_buildingManager) {
+		SDL_LogError(
+			SDL_LOG_CATEGORY_APPLICATION,
+			"Failed to create "
+			"ViewLODListManager/LegoTextureContainer/LegoCharacterManager/LegoPlantManager/LegoAnimationManager/"
+			"LegoBuildingManager"
+		);
 		goto done;
 	}
 
 	MxVariable* variable;
 
 	if (!(variable = new VisibilityVariable())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create VisibilityVariable");
 		goto done;
 	}
 	m_variableTable->SetVariable(variable);
 
 	if (!(variable = new CameraLocationVariable())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create CameraLocationVariable");
 		goto done;
 	}
 	m_variableTable->SetVariable(variable);
 
 	if (!(variable = new CursorVariable())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create CursorVariable");
 		goto done;
 	}
 	m_variableTable->SetVariable(variable);
 
 	if (!(variable = new WhoAmIVariable())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create WhoAmIVariable");
 		goto done;
 	}
 	m_variableTable->SetVariable(variable);
@@ -248,18 +265,22 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param)
 	result = RegisterWorlds();
 
 	if (result != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create RegisterWorlds");
 		goto done;
 	}
 
 	if (!(m_bkgAudioManager = new MxBackgroundAudioManager())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create MxBackgroundAudioManager");
 		goto done;
 	}
 
 	if (!(m_transitionManager = new MxTransitionManager())) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create MxTransitionManager");
 		goto done;
 	}
 
 	if (m_transitionManager->GetDDrawSurfaceFromVideoManager() != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxTransitionManager::GetDDrawSurfaceFromVideoManager failed");
 		goto done;
 	}
 
diff --git a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp
index 0b5a9e4f..8e8f34bf 100644
--- a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp
+++ b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp
@@ -19,6 +19,7 @@
 #include "tgl/d3drm/impl.h"
 #include "viewmanager/viewroi.h"
 
+#include <SDL3/SDL_log.h>
 #include <SDL3/SDL_stdinc.h>
 #include <stdio.h>
 
@@ -98,6 +99,7 @@ MxResult LegoVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyM
 		p_videoParam.SetPalette(palette);
 
 		if (!p_videoParam.GetPalette()) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxPalette::GetPalette returned NULL palette");
 			goto done;
 		}
 		paletteCreated = TRUE;
@@ -107,10 +109,12 @@ MxResult LegoVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyM
 	p_videoParam.GetPalette()->GetEntries(paletteEntries);
 
 	if (CreateDirect3D() != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "::CreateDirect3D failed");
 		goto done;
 	}
 
 	if (deviceEnumerate.DoEnumerate() != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LegoDeviceEnumerate::DoEnumerate failed");
 		goto done;
 	}
 
@@ -151,6 +155,7 @@ MxResult LegoVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyM
 			paletteEntries,
 			sizeof(paletteEntries) / sizeof(paletteEntries[0])
 		)) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxDirect3D::Create failed");
 		goto done;
 	}
 
@@ -164,18 +169,21 @@ MxResult LegoVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyM
 			p_frequencyMS,
 			p_createThread
 		) != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxVideoManager::VTable0x28 failed");
 		goto done;
 	}
 
 	m_renderer = Tgl::CreateRenderer();
 
 	if (!m_renderer) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Tgl::CreateRenderer failed");
 		goto done;
 	}
 
 	m_3dManager = new Lego3DManager;
 
 	if (!m_3dManager) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Lego3DManager::Lego3DManager failed");
 		goto done;
 	}
 
@@ -196,12 +204,14 @@ MxResult LegoVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyM
 	createStruct.m_d3dDevice = m_direct3d->Direct3DDevice();
 
 	if (!m_3dManager->Create(createStruct)) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Lego3DManager::Create failed");
 		goto done;
 	}
 
 	ViewLODList* pLODList;
 
 	if (ConfigureD3DRM() != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LegoVideoManager::ConfigureD3DRM failed");
 		goto done;
 	}
 
diff --git a/LEGO1/lego/sources/misc/legoimage.cpp b/LEGO1/lego/sources/misc/legoimage.cpp
index da3f04a0..0e72b1fe 100644
--- a/LEGO1/lego/sources/misc/legoimage.cpp
+++ b/LEGO1/lego/sources/misc/legoimage.cpp
@@ -10,38 +10,39 @@ DECOMP_SIZE_ASSERT(LegoImage, 0x310);
 // FUNCTION: LEGO1 0x100994c0
 LegoPaletteEntry::LegoPaletteEntry()
 {
-	m_red = 0;
-	m_green = 0;
-	m_blue = 0;
+	m_color.r = 0;
+	m_color.g = 0;
+	m_color.b = 0;
+	m_color.a = SDL_ALPHA_OPAQUE;
 }
 
 // FUNCTION: LEGO1 0x100994d0
 LegoResult LegoPaletteEntry::Read(LegoStorage* p_storage)
 {
 	LegoResult result;
-	if ((result = p_storage->Read(&m_red, sizeof(m_red))) != SUCCESS) {
+	if ((result = p_storage->Read(&m_color.r, sizeof(m_color.r))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Read(&m_green, sizeof(m_green))) != SUCCESS) {
+	if ((result = p_storage->Read(&m_color.g, sizeof(m_color.g))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Read(&m_blue, sizeof(m_blue))) != SUCCESS) {
+	if ((result = p_storage->Read(&m_color.b, sizeof(m_color.b))) != SUCCESS) {
 		return result;
 	}
 	return SUCCESS;
 }
 
 // FUNCTION: LEGO1 0x10099520
-LegoResult LegoPaletteEntry::Write(LegoStorage* p_storage)
+LegoResult LegoPaletteEntry::Write(LegoStorage* p_storage) const
 {
 	LegoResult result;
-	if ((result = p_storage->Write(&m_red, sizeof(m_red))) != SUCCESS) {
+	if ((result = p_storage->Write(&m_color.r, sizeof(m_color.r))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Write(&m_green, sizeof(m_green))) != SUCCESS) {
+	if ((result = p_storage->Write(&m_color.g, sizeof(m_color.g))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Write(&m_blue, sizeof(m_blue))) != SUCCESS) {
+	if ((result = p_storage->Write(&m_color.b, sizeof(m_color.b))) != SUCCESS) {
 		return result;
 	}
 	return SUCCESS;
@@ -50,84 +51,86 @@ LegoResult LegoPaletteEntry::Write(LegoStorage* p_storage)
 // FUNCTION: LEGO1 0x10099570
 LegoImage::LegoImage()
 {
-	m_width = 0;
-	m_height = 0;
-	m_count = 0;
-	m_bits = NULL;
+	m_surface = NULL;
+	m_palette = NULL;
 }
 
 // FUNCTION: LEGO1 0x100995a0
 LegoImage::LegoImage(LegoU32 p_width, LegoU32 p_height)
 {
-	m_width = p_width;
-	m_height = p_height;
-	m_count = 0;
-	m_bits = new LegoU8[m_width * m_height];
+	m_surface = SDL_CreateSurface(p_width, p_height, SDL_PIXELFORMAT_INDEX8);
+	m_palette = NULL;
 }
 
 // FUNCTION: LEGO1 0x100995f0
 LegoImage::~LegoImage()
 {
-	if (m_bits) {
-		delete[] m_bits;
-	}
+	SDL_DestroySurface(m_surface);
+	SDL_DestroyPalette(m_palette);
 }
 
 // FUNCTION: LEGO1 0x10099610
 LegoResult LegoImage::Read(LegoStorage* p_storage, LegoU32 p_square)
 {
 	LegoResult result;
-	if ((result = p_storage->Read(&m_width, sizeof(m_width))) != SUCCESS) {
+	LegoU32 width, height, count;
+	if ((result = p_storage->Read(&width, sizeof(width))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Read(&m_height, sizeof(m_height))) != SUCCESS) {
+	if ((result = p_storage->Read(&height, sizeof(height))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Read(&m_count, sizeof(m_height))) != SUCCESS) {
+	if ((result = p_storage->Read(&count, sizeof(height))) != SUCCESS) {
 		return result;
 	}
-	for (LegoU32 i = 0; i < m_count; i++) {
-		if ((result = m_palette[i].Read(p_storage)) != SUCCESS) {
+	if (m_palette) {
+		SDL_DestroyPalette(m_palette);
+	}
+	m_palette = SDL_CreatePalette(count);
+	for (LegoU32 i = 0; i < count; i++) {
+		LegoPaletteEntry paletteEntry;
+		if ((result = paletteEntry.Read(p_storage)) != SUCCESS) {
 			return result;
 		}
+		m_palette->colors[i] = paletteEntry.GetColor();
 	}
-	if (m_bits) {
-		delete[] m_bits;
+	if (m_surface) {
+		SDL_DestroySurface(m_surface);
 	}
-	m_bits = new LegoU8[m_width * m_height];
-	if ((result = p_storage->Read(m_bits, m_width * m_height)) != SUCCESS) {
+	m_surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_INDEX8);
+	if ((result = p_storage->Read(m_surface->pixels, width * height)) != SUCCESS) {
 		return result;
 	}
 
-	if (p_square && m_width != m_height) {
-		LegoU8* newBits;
+	if (p_square && width != height) {
+		SDL_Surface* newSurface;
 
-		if (m_height < m_width) {
-			LegoU32 aspect = m_width / m_height;
-			newBits = new LegoU8[m_width * m_width];
-			LegoU8* src = m_bits;
-			LegoU8* dst = newBits;
+		if (height < width) {
+			LegoU32 aspect = width / height;
+			newSurface = SDL_CreateSurface(width, width, SDL_PIXELFORMAT_INDEX8);
+			LegoU8* src = (LegoU8*) m_surface->pixels;
+			LegoU8* dst = (LegoU8*) newSurface->pixels;
 
-			for (LegoU32 row = 0; row < m_height; row++) {
+			for (LegoU32 row = 0; row < height; row++) {
 				if (aspect) {
 					for (LegoU32 dup = aspect; dup; dup--) {
-						memcpy(dst, src, m_width);
-						dst += m_width;
+						memcpy(dst, src, width);
+						dst += width;
 					}
 				}
-				src += m_width;
+				src += width;
 			}
 
-			m_height = m_width;
+			height = width;
 		}
 		else {
-			LegoU32 aspect = m_height / m_width;
-			newBits = new LegoU8[m_height * m_height];
-			LegoU8* src = m_bits;
-			LegoU8* dst = newBits;
+			LegoU32 aspect = height / width;
+			newSurface = SDL_CreateSurface(height, height, SDL_PIXELFORMAT_INDEX8);
+			LegoU8* src = (LegoU8*) m_surface->pixels;
+			LegoU8* dst = (LegoU8*) newSurface->pixels;
 
-			for (LegoU32 row = 0; row < m_height; row++) {
-				for (LegoU32 col = 0; col < m_width; col++) {
+			for (LegoU32 row = 0; row < height; row++) {
+				for (LegoU32 col = 0; col < width; col++) {
 					if (aspect) {
 						for (LegoU32 dup = aspect; dup; dup--) {
 							*dst = *src;
@@ -139,11 +142,11 @@ LegoResult LegoImage::Read(LegoStorage* p_storage, LegoU32 p_square)
 				}
 			}
 
-			m_width = m_height;
+			width = height;
 		}
 
-		delete[] m_bits;
-		m_bits = newBits;
+		SDL_DestroySurface(m_surface);
+		m_surface = newSurface;
 	}
 
 	return SUCCESS;
@@ -153,22 +156,24 @@ LegoResult LegoImage::Read(LegoStorage* p_storage, LegoU32 p_square)
 LegoResult LegoImage::Write(LegoStorage* p_storage)
 {
 	LegoResult result;
-	if ((result = p_storage->Write(&m_width, sizeof(m_width))) != SUCCESS) {
+	if ((result = p_storage->Write(&m_surface->w, sizeof(m_surface->w))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Write(&m_height, sizeof(m_height))) != SUCCESS) {
+	if ((result = p_storage->Write(&m_surface->h, sizeof(m_surface->h))) != SUCCESS) {
 		return result;
 	}
-	if ((result = p_storage->Write(&m_count, sizeof(m_height))) != SUCCESS) {
+	if ((result = p_storage->Write(&m_surface->h, sizeof(m_surface->h))) != SUCCESS) {
 		return result;
 	}
-	for (LegoU32 i = 0; i < m_count; i++) {
-		if ((result = m_palette[i].Write(p_storage)) != SUCCESS) {
-			return result;
+	if (m_palette) {
+		LegoPaletteEntry paletteEntry;
+		for (LegoU32 i = 0; i < m_palette->ncolors; i++) {
+			paletteEntry.SetColor(m_palette->colors[i]);
+			if ((result = paletteEntry.Write(p_storage)) != SUCCESS) {
+				return result;
+			}
 		}
-	}
-	if (m_bits) {
-		if ((result = p_storage->Write(m_bits, m_width * m_height)) != SUCCESS) {
+		if ((result = p_storage->Write(m_surface->pixels, m_surface->w * m_surface->h)) != SUCCESS) {
 			return result;
 		}
 	}
diff --git a/LEGO1/lego/sources/misc/legoimage.h b/LEGO1/lego/sources/misc/legoimage.h
index c62701be..ee1dc55d 100644
--- a/LEGO1/lego/sources/misc/legoimage.h
+++ b/LEGO1/lego/sources/misc/legoimage.h
@@ -3,6 +3,8 @@
 
 #include "legotypes.h"
 
+#include <SDL3/SDL_surface.h>
+
 class LegoStorage;
 
 // SIZE 0x03
@@ -10,19 +12,19 @@ class LegoPaletteEntry {
 public:
 	LegoPaletteEntry();
 	// LegoPaletteEntry(LegoU8 p_red, LegoU8 p_green, LegoU8 p_blue);
-	LegoU8 GetRed() { return m_red; }
-	void SetRed(LegoU8 p_red) { m_red = p_red; }
-	LegoU8 GetGreen() { return m_green; }
-	void SetGreen(LegoU8 p_green) { m_green = p_green; }
-	LegoU8 GetBlue() { return m_blue; }
-	void SetBlue(LegoU8 p_blue) { m_blue = p_blue; }
+	LegoU8 GetRed() const { return m_color.r; }
+	void SetRed(LegoU8 p_red) { m_color.r = p_red; }
+	LegoU8 GetGreen() const { return m_color.g; }
+	void SetGreen(LegoU8 p_green) { m_color.g = p_green; }
+	LegoU8 GetBlue() const { return m_color.b; }
+	void SetBlue(LegoU8 p_blue) { m_color.b = p_blue; }
+	SDL_Color GetColor() const { return m_color; }
+	void SetColor(SDL_Color p_color) { m_color = p_color; }
 	LegoResult Read(LegoStorage* p_storage);
-	LegoResult Write(LegoStorage* p_storage);
+	LegoResult Write(LegoStorage* p_storage) const;
 
 protected:
-	LegoU8 m_red;   // 0x00
-	LegoU8 m_green; // 0x01
-	LegoU8 m_blue;  // 0x02
+	SDL_Color m_color;
 };
 
 // 0x310
@@ -31,26 +33,29 @@ public:
 	LegoImage();
 	LegoImage(LegoU32 p_width, LegoU32 p_height);
 	~LegoImage();
-	LegoU32 GetWidth() { return m_width; }
-	void SetWidth(LegoU32 p_width) { m_width = p_width; }
-	LegoU32 GetHeight() { return m_height; }
-	void SetHeight(LegoU32 p_height) { m_height = p_height; }
-	LegoU32 GetCount() { return m_count; }
-	void SetCount(LegoU32 p_count) { m_count = p_count; }
-	LegoPaletteEntry* GetPalette() { return m_palette; }
-	LegoPaletteEntry& GetPaletteEntry(LegoU32 p_i) { return m_palette[p_i]; }
-	void SetPaletteEntry(LegoU32 p_i, LegoPaletteEntry& p_paletteEntry) { m_palette[p_i] = p_paletteEntry; }
-	LegoU8* GetBits() { return m_bits; }
-	void SetBits(LegoU8* p_bits) { m_bits = p_bits; }
+	LegoU32 GetWidth() const { return m_surface->w; }
+	LegoU32 GetHeight() const { return m_surface->h; }
+	LegoU32 GetCount() const { return m_palette ? m_palette->ncolors : 0; }
+	SDL_Palette* GetPalette() const { return m_palette; }
+	void SetPalette(SDL_Palette* p_palette)
+	{
+		SDL_DestroyPalette(m_palette);
+		m_palette = p_palette;
+	}
+	void SetPaletteEntry(LegoU32 p_i, LegoPaletteEntry& p_paletteEntry)
+	{
+		m_palette->colors[p_i] = p_paletteEntry.GetColor();
+	}
+	const LegoU8* GetBits() const { return (LegoU8*) m_surface->pixels; }
 	LegoResult Read(LegoStorage* p_storage, LegoU32 p_square);
 	LegoResult Write(LegoStorage* p_storage);
 
 protected:
-	LegoU32 m_width;                 // 0x00
-	LegoU32 m_height;                // 0x04
-	LegoU32 m_count;                 // 0x08
-	LegoPaletteEntry m_palette[256]; // 0x0c
-	LegoU8* m_bits;                  // 0x30c
+	SDL_Surface* m_surface;
+	SDL_Palette* m_palette;
+	//	LegoU32 m_count;                 // 0x08
+	//	LegoPaletteEntry m_palette[256]; // 0x0c
+	//	LegoU8* m_bits;                  // 0x30c
 };
 
 #endif // __LEGOIMAGE_H
diff --git a/LEGO1/mxdirectx/mxdirect3d.cpp b/LEGO1/mxdirectx/mxdirect3d.cpp
index 8a26f1c8..8cbb26b8 100644
--- a/LEGO1/mxdirectx/mxdirect3d.cpp
+++ b/LEGO1/mxdirectx/mxdirect3d.cpp
@@ -52,9 +52,16 @@ BOOL MxDirect3D::Create(
 		paletteEntryCount
 	);
 
+	if (!ret) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxDirectDraw::Create failed");
+	}
+
 	if (ret && D3DCreate() && D3DSetMode()) {
 		success = TRUE;
 	}
+	else {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "D3DCreate()/D3DSetMode failed");
+	}
 
 	if (!success) {
 		FUN_1009d920();
diff --git a/LEGO1/mxdirectx/mxdirectdraw.cpp b/LEGO1/mxdirectx/mxdirectdraw.cpp
index b6279636..c79d48de 100644
--- a/LEGO1/mxdirectx/mxdirectdraw.cpp
+++ b/LEGO1/mxdirectx/mxdirectdraw.cpp
@@ -2,6 +2,8 @@
 
 #include "decomp.h"
 
+#include <SDL3/SDL_log.h>
+
 DECOMP_SIZE_ASSERT(MxDirectDraw, 0x880)
 
 #define RELEASE(x)                                                                                                     \
@@ -879,6 +881,7 @@ void MxDirectDraw::Error(const char* p_message, int p_error)
 	if (!g_isInsideError) {
 		g_isInsideError = TRUE;
 		Destroy();
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxDirectDraw error: [0x%08x] %s", p_error, p_message);
 		if (m_pErrorHandler) {
 			m_pErrorHandler(p_message, p_error, m_pErrorHandlerArg);
 		}
diff --git a/LEGO1/omni/src/video/mxdisplaysurface.cpp b/LEGO1/omni/src/video/mxdisplaysurface.cpp
index 2bdfbe2d..0abd3326 100644
--- a/LEGO1/omni/src/video/mxdisplaysurface.cpp
+++ b/LEGO1/omni/src/video/mxdisplaysurface.cpp
@@ -145,6 +145,7 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 		(HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
 
 	if (!hWnd) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxDisplaySurface::Create: HWND is NULL");
 		goto done;
 	}
 
@@ -176,6 +177,7 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 		MxS32 height = m_videoParam.GetRect().GetHeight();
 
 		if (lpDirectDraw->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN)) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DirectDraw::SetCooperativeLevel failed");
 			goto done;
 		}
 
@@ -183,6 +185,7 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 		ddsd.dwSize = sizeof(ddsd);
 
 		if (lpDirectDraw->GetDisplayMode(&ddsd)) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DirectDraw::GetDisplayMode failed");
 			goto done;
 		}
 
@@ -190,6 +193,7 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 
 		if (ddsd.dwWidth != width || ddsd.dwHeight != height || ddsd.ddpfPixelFormat.dwRGBBitCount != bitdepth) {
 			if (lpDirectDraw->SetDisplayMode(width, height, bitdepth)) {
+				SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DirectDraw::SetDisplayMode failed");
 				goto done;
 			}
 		}
@@ -203,12 +207,14 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 		ddsd.ddsCaps.dwCaps = DDSCAPS_3DDEVICE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
 
 		if (lpDirectDraw->CreateSurface(&ddsd, &m_ddSurface1, NULL)) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DirectDraw::CreateSurface failed");
 			goto done;
 		}
 
 		ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
 
 		if (m_ddSurface1->GetAttachedSurface(&ddsd.ddsCaps, &m_ddSurface2)) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DirectDrawSurface::GetAttachedSurface failed");
 			goto done;
 		}
 	}
@@ -219,6 +225,7 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
 
 		if (lpDirectDraw->CreateSurface(&ddsd, &m_ddSurface1, NULL)) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DirectDraw::CreateSurface failed");
 			goto done;
 		}
 
@@ -234,6 +241,7 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 		}
 
 		if (lpDirectDraw->CreateSurface(&ddsd, &m_ddSurface2, NULL)) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DirectDraw::CreateSurface failed");
 			goto done;
 		}
 	}
@@ -246,6 +254,12 @@ MxResult MxDisplaySurface::Create(MxVideoParam& p_videoParam)
 			!m_ddSurface1->SetClipper(m_ddClipper)) {
 			result = SUCCESS;
 		}
+		else {
+			SDL_LogError(
+				SDL_LOG_CATEGORY_APPLICATION,
+				"DirectDraw::CreateClipper or DirectDrawSurface::SetClipper failed"
+			);
+		}
 	}
 
 done:
diff --git a/LEGO1/omni/src/video/mxvideomanager.cpp b/LEGO1/omni/src/video/mxvideomanager.cpp
index e6eab0e6..8392124f 100644
--- a/LEGO1/omni/src/video/mxvideomanager.cpp
+++ b/LEGO1/omni/src/video/mxvideomanager.cpp
@@ -10,6 +10,8 @@
 #include "mxticklemanager.h"
 #include "mxticklethread.h"
 
+#include <SDL3/SDL_log.h>
+
 DECOMP_SIZE_ASSERT(MxVideoManager, 0x64)
 
 // FUNCTION: LEGO1 0x100be1f0
@@ -221,6 +223,7 @@ MxResult MxVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS,
 	m_unk0x60 = TRUE;
 
 	if (MxMediaManager::Create() != SUCCESS) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxMediaManager::Create failed");
 		goto done;
 	}
 
@@ -231,10 +234,12 @@ MxResult MxVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS,
 	m_region = new MxRegion();
 
 	if (!m_region) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxRegion::MxRegion failed");
 		goto done;
 	}
 
 	if (DirectDrawCreate(NULL, &m_pDirectDraw, NULL) != DD_OK) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "::DirectDrawCreate failed");
 		goto done;
 	}
 
@@ -245,6 +250,7 @@ MxResult MxVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS,
 		NULL
 	);
 	if (m_pDirectDraw->SetCooperativeLevel(hWnd, DDSCL_NORMAL) != DD_OK) {
+		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "IDirectDraw::SetCooperativeLevel failed");
 		goto done;
 	}
 
@@ -254,6 +260,7 @@ MxResult MxVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS,
 		m_videoParam.SetPalette(palette);
 
 		if (!palette) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxPalette::MxPalette failed");
 			goto done;
 		}
 	}
@@ -262,6 +269,7 @@ MxResult MxVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS,
 		m_videoParam.SetPalette(palette);
 
 		if (!palette) {
+			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxPalette::Clone failed");
 			goto done;
 		}
 	}
@@ -274,6 +282,7 @@ MxResult MxVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS,
 			m_thread = new MxTickleThread(this, p_frequencyMS);
 
 			if (!m_thread || m_thread->Start(0, 0) != SUCCESS) {
+				SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "MxTickleThread::MxTickleThread failed");
 				goto done;
 			}
 		}
@@ -283,6 +292,12 @@ MxResult MxVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS,
 
 		status = SUCCESS;
 	}
+	else {
+		SDL_LogError(
+			SDL_LOG_CATEGORY_APPLICATION,
+			"MxDisplaySurface::MxDisplaySurface/MxDisplaySurface::Create failed"
+		);
+	}
 
 done:
 	if (status != SUCCESS) {