diff --git a/3rdParty/CMakeLists.txt b/3rdParty/CMakeLists.txt index 9838a7bba..782f163c7 100644 --- a/3rdParty/CMakeLists.txt +++ b/3rdParty/CMakeLists.txt @@ -72,8 +72,10 @@ add_library(imgui imgui/imgui_draw.cpp imgui/imgui_tables.cpp imgui/imgui_widgets.cpp + imgui/imgui_demo.cpp imgui/backends/imgui_impl_sdl.cpp imgui/backends/imgui_impl_opengl3.cpp + imgui/misc/cpp/imgui_stdlib.cpp ) target_link_libraries(imgui diff --git a/CMakeLists.txt b/CMakeLists.txt index b66f358d7..af2a6ed7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/AliveLibAO) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/AliveLibAE) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/relive) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/Tools/vab_tool) +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/Tools/asset_tool) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/Tools/relive_api) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/Tools/relive_api_integration_test) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Source/Tools/relive_api_unit_test) diff --git a/Source/AliveLibAE/Animation.cpp b/Source/AliveLibAE/Animation.cpp index 286503749..d21d225c6 100644 --- a/Source/AliveLibAE/Animation.cpp +++ b/Source/AliveLibAE/Animation.cpp @@ -20,6 +20,7 @@ #include "Sys_common.hpp" #include "Renderer/IRenderer.hpp" #include +#include "ExternalAssets.hpp" // Frame call backs ?? EXPORT s32 CC Animation_OnFrame_Common_Null_455F40(void*, s16*) @@ -602,6 +603,24 @@ void Animation::vRender_40B820(s32 xpos, s32 ypos, PrimHeader** ppOt, s16 width, SetPrimExtraPointerHack(pPoly, nullptr); } + ////////////// + // HD HAX + ////////////// + + CustomRenderSpriteFormat sprite; + sprite.x = PsxToPCX(xpos); + sprite.y = ypos; + sprite.frame = (this->field_92_current_frame == -1) ? 0 : this->field_92_current_frame; + sprite.frametable_offset = field_18_frame_table_offset; + sprite.resource_id = pResourceManager_5C1BB0->Get_Header_49C410(field_20_ppBlock)->field_C_id; + sprite.r = pPoly->mBase.header.rgb_code.r; + sprite.g = pPoly->mBase.header.rgb_code.g; + sprite.b = pPoly->mBase.header.rgb_code.b; + sprite.scale = (float)FP_GetDouble(this->field_14_scale); + sprite.flip = this->field_4_flags.Get(AnimFlags::eBit5_FlipX); + + CustomRender_AddCommand(pPoly, sprite); + OrderingTable_Add_4F8AA0(OtLayer(ppOt, field_C_render_layer), &pPoly->mBase.header); } diff --git a/Source/AliveLibAE/BaseBomb.cpp b/Source/AliveLibAE/BaseBomb.cpp index 77baa388d..2eb7e03c8 100644 --- a/Source/AliveLibAE/BaseBomb.cpp +++ b/Source/AliveLibAE/BaseBomb.cpp @@ -30,7 +30,7 @@ BaseBomb* BaseBomb::ctor_423E70(FP x, FP y, s32 /*unused*/, FP scale) SetVTable(this, 0x544C54); SetType(AETypes::eBaseBomb_46); - const AnimRecord& rec = AnimRec(AnimId::Explosion_Mine); + const AnimRecord& rec = AnimRec(AnimId::GroundExplosion); Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, BaseGameObject::Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId), 1, 1); field_20_animation.field_4_flags.Clear(AnimFlags::eBit18_IsLastFrame); // Double Check @@ -192,7 +192,7 @@ void BaseBomb::vUpdate_424180() if (field_20_animation.field_92_current_frame == 3) { - const AnimRecord& rec = AnimRec(AnimId::Explosion_Mine); + const AnimRecord& rec = AnimRec(AnimId::GroundExplosion); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); if (ppRes) { diff --git a/Source/AliveLibAE/CMakeLists.txt b/Source/AliveLibAE/CMakeLists.txt index e649bdba8..131541534 100644 --- a/Source/AliveLibAE/CMakeLists.txt +++ b/Source/AliveLibAE/CMakeLists.txt @@ -433,6 +433,7 @@ endif() target_link_libraries(AliveLibAE AliveLibCommon + jsonxx_reliveapi EasyLogging++ googletest Cinder diff --git a/Source/AliveLibAE/DoorFlame.cpp b/Source/AliveLibAE/DoorFlame.cpp index 11fde152e..208e76996 100644 --- a/Source/AliveLibAE/DoorFlame.cpp +++ b/Source/AliveLibAE/DoorFlame.cpp @@ -170,7 +170,7 @@ class FlameSparks final : public ::BaseAnimatedWithPhysicsGameObject SetVTable(this, 0x545974); SetType(AETypes::eNone_0); - const AnimRecord& rec = AnimRec(AnimId::Zap_Sparks); + const AnimRecord& rec = AnimRec(AnimId::ChantOrb_Particle_Small); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1, 1); diff --git a/Source/AliveLibAE/Explosion.cpp b/Source/AliveLibAE/Explosion.cpp index c2674ad58..5639c7b36 100644 --- a/Source/AliveLibAE/Explosion.cpp +++ b/Source/AliveLibAE/Explosion.cpp @@ -23,13 +23,13 @@ Explosion* Explosion::ctor_4A1200(FP xpos, FP ypos, FP scale, s16 bSmall) field_F4_bSmall = bSmall; if (field_F4_bSmall) { - const AnimRecord& rec = AnimRec(AnimId::Explosion_Small); + const AnimRecord& rec = AnimRec(AnimId::AirExplosion_Small); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1, 1); } else { - const AnimRecord& rec = AnimRec(AnimId::Explosion); + const AnimRecord& rec = AnimRec(AnimId::AirExplosion); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1, 1); } @@ -179,7 +179,7 @@ void Explosion::vUpdate_4A1510() auto pParticle = ae_new(); if (pParticle) { - const AnimRecord& rec = field_F4_bSmall ? AnimRec(AnimId::Explosion_Small) : AnimRec(AnimId::Explosion); + const AnimRecord& rec = field_F4_bSmall ? AnimRec(AnimId::AirExplosion_Small) : AnimRec(AnimId::AirExplosion); pParticle->ctor_4CC4C0(field_B8_xpos, field_BC_ypos, rec.mFrameTableOffset, 202, // Same size for both explosions for some reason 91, // ^^^ diff --git a/Source/AliveLibAE/FallingItem.cpp b/Source/AliveLibAE/FallingItem.cpp index ae258cb33..cbacc2e89 100644 --- a/Source/AliveLibAE/FallingItem.cpp +++ b/Source/AliveLibAE/FallingItem.cpp @@ -377,7 +377,7 @@ EXPORT void FallingItem::vUpdate_427780() auto pParticle = ae_new(); if (pParticle) { - const AnimRecord& rec = AnimRec(AnimId::Explosion); + const AnimRecord& rec = AnimRec(AnimId::AirExplosion); u8** ppRes = ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Animation, rec.mResourceId, 0, 0); pParticle->ctor_4CC4C0( field_B8_xpos, diff --git a/Source/AliveLibAE/FlyingSlig.cpp b/Source/AliveLibAE/FlyingSlig.cpp index b36a1ccdf..52f4d7daf 100644 --- a/Source/AliveLibAE/FlyingSlig.cpp +++ b/Source/AliveLibAE/FlyingSlig.cpp @@ -138,7 +138,7 @@ FlyingSlig* FlyingSlig::ctor_4342B0(Path_FlyingSlig* pTlv, s32 tlvInfo) field_10_resources_array.SetAt(7, ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Animation, AEResourceID::kVaporResID, TRUE, FALSE)); field_10_resources_array.SetAt(8, ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Animation, AEResourceID::kSlogBlowResID, TRUE, FALSE)); - const AnimRecord& rec = AnimRec(AnimId::Flying_Slig_Idle); + const AnimRecord& rec = AnimRec(AnimId::FlyingSlig_Idle); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1, 1); //Animation_Init_424E10(116888, 107, 48u, field_10_resources_array.ItemAt(0), 1, 1u); @@ -261,23 +261,23 @@ FlyingSlig* FlyingSlig::ctor_4342B0(Path_FlyingSlig* pTlv, s32 tlvInfo) } const AnimId sFlyingSligFrameTables_552408[28] = { - AnimId::Flying_Slig_Idle, - AnimId::Flying_Slig_Move_Horizontal, - AnimId::Flying_Slig_Idle_Turn_Around, - AnimId::Flying_Slig_Move_Down, - AnimId::Flying_Slig_Move_Down_Turn_Around, - AnimId::Flying_Slig_Move_Up, - AnimId::Flying_Slig_Move_Up_Turn_Around, - AnimId::Flying_Slig_Pull_Lever, - AnimId::Flying_Slig_Speak, - AnimId::Flying_Slig_Possession, - AnimId::Flying_Slig_Move_Horizontal_End, - AnimId::Flying_Slig_Move_Up_Start, - AnimId::Flying_Slig_Move_Horizontal_To_Down, - AnimId::Flying_Slig_Move_Up_To_Horizontal, - AnimId::Flying_Slig_Move_Down_To_Horizontal, - AnimId::Flying_Slig_Turn_Quick, - AnimId::Flying_Slig_Idle_To_Horizontal, + AnimId::FlyingSlig_Idle, + AnimId::FlyingSlig_MoveHorizontal, + AnimId::FlyingSlig_IdleTurnAround, + AnimId::FlyingSlig_MoveDown, + AnimId::FlyingSlig_MoveDownTurnAround, + AnimId::FlyingSlig_MoveUp, + AnimId::FlyingSlig_MoveUpTurnAround, + AnimId::FlyingSlig_PullLever, + AnimId::FlyingSlig_Speak, + AnimId::FlyingSlig_Possession, + AnimId::FlyingSlig_MoveHorizontalEnd, + AnimId::FlyingSlig_MoveUpStart, + AnimId::FlyingSlig_MoveHorizontalToDown, + AnimId::FlyingSlig_MoveUpToHorizontal, + AnimId::FlyingSlig_MoveDownToHorizontal, + AnimId::FlyingSlig_TurnQuick, + AnimId::FlyingSlig_IdleToHorizontal, AnimId::FlyingSlig_BeginDownMovement, AnimId::FlyingSlig_EndDownMovement, AnimId::FlyingSlig_DownKnockback, diff --git a/Source/AliveLibAE/Game.cpp b/Source/AliveLibAE/Game.cpp index 7dd8ef757..f007fe0d2 100644 --- a/Source/AliveLibAE/Game.cpp +++ b/Source/AliveLibAE/Game.cpp @@ -212,7 +212,7 @@ EXPORT void CC DrawFps_4952F0(Bitmap* pBmp, s32 x, s32 y, f32 fps) EXPORT void CC sub_4FBA20() { - NOT_IMPLEMENTED(); + //NOT_IMPLEMENTED(); } EXPORT void CC CheckShiftCapslock_4953B0() diff --git a/Source/AliveLibAE/Movie.cpp b/Source/AliveLibAE/Movie.cpp index f1e8971c7..f33a43f45 100644 --- a/Source/AliveLibAE/Movie.cpp +++ b/Source/AliveLibAE/Movie.cpp @@ -673,14 +673,18 @@ void Movie::Init_4DFF60(s32 id, CdlLOC* pCdPos, s16 bUnknown, s16 flags, s16 vol ResourceManager::Reclaim_Memory_49C470(0); } -Movie* Movie::ctor_4DFDE0(s32 id, u32 pos, s16 bUnknown, s16 flags, s16 volume) +Movie* Movie::ctor_4DFDE0(s32 /*id*/, u32 /*pos*/, s16 /*bUnknown*/, s16 /*flags*/, s16 /*volume*/) { + /* BaseGameObject_ctor_4DBFA0(TRUE, 0); SetVTable(this, 0x547EF4); // vTbl_Movie_547EF4 CdlLOC cdLoc = {}; PSX_Pos_To_CdLoc_4FADD0(pos, &cdLoc); Init_4DFF60(id, &cdLoc, bUnknown, flags, volume); + */ + + field_6_flags.Set(BaseGameObject::Options::eDead_Bit3); return this; } diff --git a/Source/AliveLibAE/Paramite.cpp b/Source/AliveLibAE/Paramite.cpp index fd7653af0..db9257bce 100644 --- a/Source/AliveLibAE/Paramite.cpp +++ b/Source/AliveLibAE/Paramite.cpp @@ -279,7 +279,7 @@ const AnimId sParamiteAnimIdTable_55D660[44] = { AnimId::Paramite_WebLeaveUp, AnimId::Paramite_Eating, AnimId::Paramite_Death, - AnimId::Paramite_Squawk, + AnimId::Paramite_Struggle, AnimId::Paramite_Attack}; s32 CC Paramite::CreateFromSaveState_4855A0(const u8* pBuffer) diff --git a/Source/AliveLibAE/PsxRender.cpp b/Source/AliveLibAE/PsxRender.cpp index bd332d7e4..65c1c1e30 100644 --- a/Source/AliveLibAE/PsxRender.cpp +++ b/Source/AliveLibAE/PsxRender.cpp @@ -3432,6 +3432,8 @@ static bool DrawOTagImpl(PrimHeader** ppOt, s16 drawEnv_of0, s16 drawEnv_of1) pOtItem = any.mPrimHeader->tag; // offset 0 } + renderer.EndFrame(); + return false; } diff --git a/Source/AliveLibAE/Renderer/DirectX9Renderer.cpp b/Source/AliveLibAE/Renderer/DirectX9Renderer.cpp index 2ebb9f3b8..1694e2f2c 100644 --- a/Source/AliveLibAE/Renderer/DirectX9Renderer.cpp +++ b/Source/AliveLibAE/Renderer/DirectX9Renderer.cpp @@ -83,6 +83,10 @@ void DirectX9Renderer::StartFrame(s32 /*xOff*/, s32 /*yOff*/) } void DirectX9Renderer::EndFrame() +{ +} + +void DirectX9Renderer::Present() { SDL_RenderPresent(mRenderer); } @@ -180,4 +184,9 @@ void DirectX9Renderer::Upload(BitDepth /*bitDepth*/, const PSX_RECT& /*rect*/, c { } +void DirectX9Renderer::LoadExternalCam(const char* /*path*/, const unsigned char* /*key*/, int /*keyLength*/) +{ + LOG_WARNING("LoadExternalCam not implemented for DirectX9 - external cam not loaded."); +} + #endif diff --git a/Source/AliveLibAE/Renderer/DirectX9Renderer.hpp b/Source/AliveLibAE/Renderer/DirectX9Renderer.hpp index e46acc193..3ed2108b4 100644 --- a/Source/AliveLibAE/Renderer/DirectX9Renderer.hpp +++ b/Source/AliveLibAE/Renderer/DirectX9Renderer.hpp @@ -12,6 +12,7 @@ class DirectX9Renderer final : public IRenderer void Clear(u8 r, u8 g, u8 b) override; void StartFrame(s32 xOff, s32 yOff) override; void EndFrame() override; + void Present() override; void BltBackBuffer(const SDL_Rect* pCopyRect, const SDL_Rect* pDst) override; void OutputSize(s32* w, s32* h) override; bool UpdateBackBuffer(const void* pPixels, s32 pitch) override; @@ -36,6 +37,8 @@ class DirectX9Renderer final : public IRenderer void Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* pPixels) override; + void LoadExternalCam(const char* path, const unsigned char* key, int keyLength) override; + private: SDL_Renderer* mRenderer = nullptr; IDirect3DDevice9* mDevice = nullptr; diff --git a/Source/AliveLibAE/Renderer/IRenderer.cpp b/Source/AliveLibAE/Renderer/IRenderer.cpp index df8e28593..c5ed5936c 100644 --- a/Source/AliveLibAE/Renderer/IRenderer.cpp +++ b/Source/AliveLibAE/Renderer/IRenderer.cpp @@ -3,12 +3,12 @@ #include "SoftwareRenderer.hpp" #include "DirectX9Renderer.hpp" +static IRenderer* gRenderer = nullptr; + #if RENDERER_OPENGL #include "OpenGLRenderer.hpp" #endif -static IRenderer* gRenderer = nullptr; - IRenderer* IRenderer::GetRenderer() { return gRenderer; diff --git a/Source/AliveLibAE/Renderer/IRenderer.hpp b/Source/AliveLibAE/Renderer/IRenderer.hpp index b108c2896..b142499f0 100644 --- a/Source/AliveLibAE/Renderer/IRenderer.hpp +++ b/Source/AliveLibAE/Renderer/IRenderer.hpp @@ -12,6 +12,7 @@ class IRenderer public: enum class Renderers { + None = 0, Software, DirectX9, OpenGL, @@ -43,6 +44,7 @@ class IRenderer virtual void Clear(u8 r, u8 g, u8 b) = 0; virtual void StartFrame(s32 xOff, s32 yOff) = 0; virtual void EndFrame() = 0; + virtual void Present() = 0; virtual void BltBackBuffer(const SDL_Rect* pCopyRect, const SDL_Rect* pDst) = 0; virtual void OutputSize(s32* w, s32* h) = 0; virtual bool UpdateBackBuffer(const void* pPixels, s32 pitch) = 0; @@ -59,6 +61,8 @@ class IRenderer virtual void Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* pPixels) = 0; + virtual void LoadExternalCam(const char* path, const unsigned char* key, int keyLength) = 0; + // FG1/zaplines/blood/hintfly virtual void Draw(Prim_Sprt& sprt) = 0; virtual void Draw(Prim_GasEffect& gasEffect) = 0; diff --git a/Source/AliveLibAE/Renderer/OpenGLRenderer.cpp b/Source/AliveLibAE/Renderer/OpenGLRenderer.cpp index 64f51d59f..9933d1c44 100644 --- a/Source/AliveLibAE/Renderer/OpenGLRenderer.cpp +++ b/Source/AliveLibAE/Renderer/OpenGLRenderer.cpp @@ -3,12 +3,49 @@ #include "OpenGLRenderer.hpp" #include "Compression.hpp" #include "VRam.hpp" +#include "AnimResources.hpp" +#include "ExternalAssets.hpp" +#include "../Tools/asset_tool/asset_common.hpp" #include "StbImageImplementation.hpp" +#include +#include + +#define MAGIC_ENUM_RANGE_MIN 0 +#define MAGIC_ENUM_RANGE_MAX 1024 +#include "magic_enum/include/magic_enum.hpp" + +namespace fs = std::filesystem; +using namespace std::string_literals; + #define GL_TO_IMGUI_TEX(v) *reinterpret_cast(&v) -static GLuint mBackgroundTexture = 0; +const char* gExternalAssetPath = "hd/"; +static OpenGLRenderer* gGLInstance = nullptr; + +struct FrameBuffer +{ + GLuint handle; + GLuint texture_handle; + int width; + int height; +}; + +struct DebugDrawText +{ + std::string text; + int screen_space_x; + int screen_space_y; + Uint32 color; +}; + +static std::vector gDebugTexts; + +static GLuint mHDBackgroundTexture = 0; +static GLuint mStitchedBackground = 0; +static FrameBuffer mWindowFrameBuffer = {}; +static FrameBuffer mTempSpriteBuffer = {}; static u8 gDecodeBuffer[640 * 256 * 2] = {}; static GLuint gDecodedTextureCache = 0; @@ -28,6 +65,187 @@ static bool gRenderEnable_F4 = true; static bool gRenderEnable_F3 = true; static bool gRenderEnable_F2 = true; +bool gExternalTexturesEnabled = true; +static bool gGLDebugInfo = false; +static bool gShowAnimIDs = false; + +struct ExternalTexture final +{ + GLuint handle; + int width; + int height; +}; + +struct ExternalTextureMeta final +{ + std::vector textures; + std::map textures_emissive; + std::map textures_flipped; + std::map textures_flipped_emissive; + AssetMeta meta; +}; + +std::map gLoadedExternalTextures; + +std::string ReadFileToString(std::string fileName) +{ + std::ifstream file; + file.open(fileName); + std::stringstream buffer; + buffer << file.rdbuf(); + file.close(); + return buffer.str(); +} + +ExternalTexture LoadTextureCacheFile(const std::string& path) +{ + int x = 0, y = 0, comp = 0; + + std::vector fileData; + + // Try to keep all paths and filenames lowercase for our linux friends. + std::ifstream file(path, std::ios::binary); + + if (file.is_open()) + { + fileData = std::vector((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + } + + const unsigned char* data = stbi_load_from_memory(fileData.data(), static_cast(fileData.size()), &x, &y, &comp, 4); + + if (comp == 0) + { + ExternalTexture nullt; + nullt.handle = 0; + nullt.width = 10; + nullt.height = 10; + return nullt; + } + + GLuint newTexture = 0; + glGenTextures(1, &newTexture); + + glBindTexture(GL_TEXTURE_2D, newTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + stbi_image_free((void*)data); + + ExternalTexture textureCache; + textureCache.handle = newTexture; + textureCache.width = x; + textureCache.height = y; + + return textureCache; +} + +void LoadAllExternalTextures(const std::string& dir = "hd/sprites") +{ + const std::string asset_suffix_emissive = "_emissive.png"; + const std::string asset_suffix_flipped = "_flipped.png"; + const std::string asset_suffix_flipped_emissive = "_flipped_emissive.png"; + + static const auto invalidEnum = magic_enum::enum_cast(-1); + + u32 hdFoldersCount = 0; + for (const auto& rootDir : fs::directory_iterator(dir)) + { + const std::string folderName = rootDir.path().lexically_relative(dir).string(); + if (rootDir.is_directory() && magic_enum::enum_cast(folderName) != invalidEnum) + { + hdFoldersCount++; + } + } + + u32 loadedHdFoldersCount = 0; + for (const auto& rootDir : fs::directory_iterator(dir)) + { + if (rootDir.is_directory()) + { + const std::string folderName = rootDir.path().lexically_relative(dir).string(); + + printf("[%d/%d] Loading animation %s from external directory\n", loadedHdFoldersCount + 1, hdFoldersCount, folderName.c_str()); + + std::string assetDirectory = dir + "/" + folderName + "/"; + + // check if with magic_enum if we have a valid enum + if (magic_enum::enum_cast(folderName) != invalidEnum) + { + AnimId id = magic_enum::enum_cast(folderName).value(); + + if (!gLoadedExternalTextures[id].meta.LoadJSONFromFile(assetDirectory + "meta.json")) + { + LOG_ERROR("Failed to load animation meta json for " << folderName); + ALIVE_FATAL("Failed to load animation meta json"); + } + + gLoadedExternalTextures[id].textures = std::vector(); + gLoadedExternalTextures[id].textures.reserve(gLoadedExternalTextures[id].meta.frame_count); + + for (int i = 0; i < gLoadedExternalTextures[id].meta.frame_count; i++) + { + std::string frameBaseName = assetDirectory + std::to_string(i); + + gLoadedExternalTextures[id].textures.push_back(LoadTextureCacheFile(frameBaseName + ".png")); + + std::string emissive = frameBaseName + asset_suffix_emissive; + std::string flipped = frameBaseName + asset_suffix_flipped; + std::string flippedEmissive = frameBaseName + asset_suffix_flipped_emissive; + + // check for our alternative textures + if (fs::exists(emissive)) + gLoadedExternalTextures[id].textures_emissive[i] = LoadTextureCacheFile(emissive); + if (fs::exists(flipped)) + gLoadedExternalTextures[id].textures_flipped[i] = LoadTextureCacheFile(flipped); + if (fs::exists(flippedEmissive)) + gLoadedExternalTextures[id].textures_flipped_emissive[i] = LoadTextureCacheFile(flippedEmissive); + } + + loadedHdFoldersCount++; + } + else + { + printf("Invalid External Dir Name: %s\n", folderName.c_str()); + } + } + } +} + +/* + +#define GL_CHECK(stmt) do { \ + stmt; \ + CheckOpenGLError(#stmt, __FILE__, __LINE__); \ +} while (0) + + +#define glEnable(...) GL_CHECK(glEnable(__VA_ARGS__)); +#define glGenTextures(...) GL_CHECK(glGenTextures(__VA_ARGS__)); +#define glBindTexture(...) GL_CHECK(glBindTexture(__VA_ARGS__)); +#define glTexParameteri(...) GL_CHECK(glTexParameteri(__VA_ARGS__)); +#define glTexImage2D(...) GL_CHECK(glTexImage2D(__VA_ARGS__)); +#define glBlendFunc(...) GL_CHECK(glBlendFunc(__VA_ARGS__)); +#define glDrawElements(...) GL_CHECK(glDrawElements(__VA_ARGS__)); +*/ + +void CheckOpenGLError(const char* stmt, const char* fname, int line) +{ + GLenum err = glGetError(); + if (err != GL_NO_ERROR) + { + std::string message; + message.resize(5000); + sprintf(message.data(), "OpenGL error %08x, at %s:%i - for %s\n", err, fname, line, stmt); + ALIVE_FATAL(message.c_str()); + } +} + static GLuint TextureFromFile(const char_type* path) { GLuint texHandle = 0; @@ -63,27 +281,19 @@ static GLuint TextureFromFile(const char_type* path) static GLuint GetBackgroundTexture() { - if (mBackgroundTexture != 0) + if (mHDBackgroundTexture != 0 && gExternalTexturesEnabled) { - return mBackgroundTexture; + return mHDBackgroundTexture; } - for (TextureCache& t : gRendererTextures) - { - if (t.mVramRect.h == 240) - { - return t.mTextureID; - } - } - - return 0; + return mStitchedBackground; } static TextureCache* GetBackgroundTextureCache() { gFakeTextureCache.mPalXY = {}; gFakeTextureCache.mVramRect = {0, 0, 640, 240}; - gFakeTextureCache.mTextureID = mBackgroundTexture; + gFakeTextureCache.mTextureID = GetBackgroundTexture(); gFakeTextureCache.mPalNormMulti = 0; gFakeTextureCache.mIsFG1 = false; gFakeTextureCache.mUvOffset = {}; @@ -431,9 +641,15 @@ static TextureCache* Renderer_TextureFromAnim(Poly_FT4& poly) if (gDecodedTextureCache == 0) gDecodedTextureCache = Renderer_CreateTexture(); - u16 tWidth = reinterpret_cast(GetPrimExtraPointerHack(&poly))[0]; + u16 tWidth = static_cast(ceil(reinterpret_cast(GetPrimExtraPointerHack(&poly))[0] / 4.0f) * 4);; u16 tHeight = reinterpret_cast(GetPrimExtraPointerHack(&poly))[1]; + /*if (tWidth > 4096 || tHeight > 4096) + { + LOG_INFO("Tried decompressing an anim but got an unusual width/height\nReturning to prevent crash."); + return nullptr; + }*/ + TPageMode textureMode = static_cast(((u32) poly.mVerts[0].mUv.tpage_clut_pad >> 7) & 3); gFakeTextureCache = {}; @@ -451,7 +667,7 @@ static TextureCache* Renderer_TextureFromAnim(Poly_FT4& poly) // So we scale it back down for the shader. gFakeTextureCache.mPalNormMulti = 16; glBindTexture(GL_TEXTURE_2D, gDecodedTextureCache); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, (tWidth + 1) / 2, tHeight, 0, GL_RED, GL_UNSIGNED_BYTE, gDecodeBuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, tWidth / 2, tHeight, 0, GL_RED, GL_UNSIGNED_BYTE, gDecodeBuffer); break; case TPageMode::e8Bit_1: gFakeTextureCache.mBitDepth = IRenderer::BitDepth::e16Bit; @@ -474,17 +690,51 @@ static TextureCache* Renderer_TextureFromAnim(Poly_FT4& poly) return &gFakeTextureCache; } -void OpenGLRenderer::DrawTexture(GLuint pTexture, f32 x, f32 y, f32 width, f32 height) +void OpenGLRenderer::DrawTexturePalette(GLuint pTexture, f32 x, f32 y, f32 width, f32 height, glm::vec3 color, glm::vec2 uv0, glm::vec2 uv1, PaletteCache * palette, int palDepth) { - const f32 r = 1.0f; - const f32 g = 1.0f; - const f32 b = 1.0f; + const f32 r = color.r; + const f32 g = color.g; + const f32 b = color.b; const VertexData verts[4] = { - {0, 0, 0, r, g, b, 0, 0}, - {1, 0, 0, r, g, b, 1, 0}, - {1, 1, 0, r, g, b, 1, 1}, - {0, 1, 0, r, g, b, 0, 1}}; + {0, 0, 0, r, g, b, uv0.x, uv0.y}, + {1, 0, 0, r, g, b, uv1.x, uv0.y}, + {1, 1, 0, r, g, b, uv1.x, uv1.y}, + {0, 1, 0, r, g, b, uv0.x, uv1.y} }; + + mTextureShader.Use(); + + mTextureShader.UniformMatrix4fv("m_MVP", GetMVP(x, y, width, height)); + mTextureShader.Uniform1i("m_Sprite", 0); // Set m_Sprite to GL_TEXTURE0 + mTextureShader.Uniform1i("m_Palette", 1); // Set m_Sprite to GL_TEXTURE1 + mTextureShader.Uniform1i("m_PaletteEnabled", true); + mTextureShader.Uniform1i("m_Textured", true); + mTextureShader.Uniform1i("m_PaletteDepth", palDepth); + + Renderer_BindPalette(palette); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, pTexture); + + + + const GLuint indexData[6] = { 0, 1, 3, 3, 1, 2 }; + DrawTriangles(verts, 4, indexData, 6); + + mTextureShader.UnUse(); +} + +void OpenGLRenderer::DrawTexture(GLuint pTexture, f32 x, f32 y, f32 width, f32 height, glm::vec3 color, glm::vec2 uv0, glm::vec2 uv1) +{ + const f32 r = color.r; + const f32 g = color.g; + const f32 b = color.b; + + const VertexData verts[4] = { + {0, 0, 0, r, g, b, uv0.x, uv0.y}, + {1, 0, 0, r, g, b, uv1.x, uv0.y}, + {1, 1, 0, r, g, b, uv1.x, uv1.y}, + {0, 1, 0, r, g, b, uv0.x, uv1.y} }; mTextureShader.Use(); @@ -496,12 +746,17 @@ void OpenGLRenderer::DrawTexture(GLuint pTexture, f32 x, f32 y, f32 width, f32 h glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, pTexture); - const GLuint indexData[6] = {0, 1, 3, 3, 1, 2}; + const GLuint indexData[6] = { 0, 1, 3, 3, 1, 2 }; DrawTriangles(verts, 4, indexData, 6); mTextureShader.UnUse(); } +void OpenGLRenderer::DrawTexture(GLuint pTexture, f32 x, f32 y, f32 width, f32 height, glm::vec2 uv0, glm::vec2 uv1) +{ + DrawTexture(pTexture, x, y, width, height, glm::vec3(1, 1, 1), uv0, uv1); +} + void OpenGLRenderer::InitAttributes() { @@ -586,7 +841,14 @@ void OpenGLRenderer::DebugWindow() if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("Developer")) + if (ImGui::BeginMenu("Debug")) + { + ImGui::MenuItem("Enable OpenGL Info", nullptr, &gGLDebugInfo); + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Rendering")) { if (ImGui::BeginMenu("Render Mode")) { @@ -616,88 +878,207 @@ void OpenGLRenderer::DebugWindow() ImGui::EndMenu(); } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("External Assets")) + { + ImGui::MenuItem("Show Anim IDs", nullptr, &gShowAnimIDs); + ImGui::MenuItem("External Textures Enabled", nullptr, &gExternalTexturesEnabled); + + if (ImGui::MenuItem("Reload External Textures")) + { + for (auto& extTexture : gLoadedExternalTextures) + { + // Free up all our currently loaded textures + + for (auto t : extTexture.second.textures) + { + glDeleteTextures(1, &t.handle); + } + + for (auto t : extTexture.second.textures_flipped) + { + glDeleteTextures(1, &t.second.handle); + } + + // Free emissive textures + for (auto t : extTexture.second.textures_emissive) + { + glDeleteTextures(1, &t.second.handle); + } + + for (auto t : extTexture.second.textures_flipped_emissive) + { + glDeleteTextures(1, &t.second.handle); + } + } + + gLoadedExternalTextures.clear(); + + LoadAllExternalTextures(); + } + ImGui::EndMenu(); } + ImGui::EndMainMenuBar(); } //ImGui::ShowDemoWindow(); - if (ImGui::Begin("Texture Window", nullptr, ImGuiWindowFlags_MenuBar)) + if (ImGui::Begin("External Meta Editor")) { - f32 widthSpace = ImGui::GetContentRegionAvailWidth(); - f32 currentWidth = 0; - for (size_t i = 0; i < gRendererTextures.size(); i++) + // create a combo box to select meta in gLoadedExternalTextures + static AnimId selectedMeta = AnimId::None; + + if (ImGui::BeginCombo("Texture", magic_enum::enum_name(selectedMeta).data())) { - f32 textureWidth = static_cast(gRendererTextures[i].mVramRect.w); - f32 textureHeight = static_cast(gRendererTextures[i].mVramRect.h); + for (auto& meta : gLoadedExternalTextures) + { + bool is_selected = (selectedMeta == meta.first); + if (ImGui::Selectable(magic_enum::enum_name(meta.first).data(), is_selected)) + { + selectedMeta = meta.first; + } - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint - ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } - if (currentWidth >= widthSpace) - currentWidth = 0; - else - ImGui::SameLine(); + if (selectedMeta != AnimId::None) + { + ImGui::Separator(); - ImGui::Image(GL_TO_IMGUI_TEX(gRendererTextures[i].mTextureID), {textureWidth, textureHeight}); - ImVec2 pos = ImGui::GetCursorScreenPos(); - if (ImGui::IsItemHovered()) + // edit all the attributes of the selected meta + ImGui::DragInt2("Ref Image Size", &gLoadedExternalTextures[selectedMeta].meta.size_width); + ImGui::DragInt2("Offset", &gLoadedExternalTextures[selectedMeta].meta.offset_x); + + ImGui::Separator(); + + if (ImGui::Button("Save")) { - ImGui::BeginTooltip(); - ImGui::Text("%d, %d, %d, %d", gRendererTextures[i].mVramRect.x, gRendererTextures[i].mVramRect.y, gRendererTextures[i].mVramRect.w, gRendererTextures[i].mVramRect.h); - ImVec2 uv0 = ImVec2(0.0f, 0.0f); - ImVec2 uv1 = ImVec2(1.0f, 1.0f); - ImGui::Image(GL_TO_IMGUI_TEX(gRendererTextures[i].mTextureID), ImVec2(textureWidth * 4, textureHeight * 4), uv0, uv1, tint_col, border_col); - ImGui::EndTooltip(); + gLoadedExternalTextures[selectedMeta].meta.SaveJSONToFile(std::string(gExternalAssetPath) + "sprites/" + std::string(magic_enum::enum_name(selectedMeta)) + "/meta.json"); } - ImVec2 imgSize = ImGui::GetItemRectSize(); - currentWidth += imgSize.x + style.ItemSpacing.x; } } ImGui::End(); - if (ImGui::Begin("Palettes", nullptr, ImGuiWindowFlags_MenuBar)) + if (gGLDebugInfo) { - f32 width = ImGui::GetWindowContentRegionWidth(); - for (auto& pal : gRendererPals) + if (ImGui::Begin("Texture Window", nullptr, ImGuiWindowFlags_MenuBar)) { - ImGui::Image(GL_TO_IMGUI_TEX(pal.mPalTextureID), ImVec2(width, 16)); + f32 widthSpace = ImGui::GetContentRegionAvailWidth(); + f32 currentWidth = 0; + for (size_t i = 0; i < gRendererTextures.size(); i++) + { + f32 textureWidth = static_cast(gRendererTextures[i].mVramRect.w); + f32 textureHeight = static_cast(gRendererTextures[i].mVramRect.h); + + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white + + if (currentWidth >= widthSpace) + currentWidth = 0; + else + ImGui::SameLine(); + + ImGui::Image(GL_TO_IMGUI_TEX(gRendererTextures[i].mTextureID), { textureWidth, textureHeight }); + ImVec2 pos = ImGui::GetCursorScreenPos(); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("%d, %d, %d, %d", gRendererTextures[i].mVramRect.x, gRendererTextures[i].mVramRect.y, gRendererTextures[i].mVramRect.w, gRendererTextures[i].mVramRect.h); + ImVec2 uv0 = ImVec2(0.0f, 0.0f); + ImVec2 uv1 = ImVec2(1.0f, 1.0f); + ImGui::Image(GL_TO_IMGUI_TEX(gRendererTextures[i].mTextureID), ImVec2(textureWidth * 4, textureHeight * 4), uv0, uv1, tint_col, border_col); + ImGui::EndTooltip(); + } + ImVec2 imgSize = ImGui::GetItemRectSize(); + currentWidth += imgSize.x + style.ItemSpacing.x; + } } - } - ImGui::End(); + ImGui::End(); - if (ImGui::Begin("VRAM", nullptr, ImGuiWindowFlags_MenuBar)) - { - ImVec2 pos = ImGui::GetWindowPos(); + if (ImGui::Begin("GPU Info", nullptr, ImGuiWindowFlags_MenuBar)) + { + + ImGui::Text("Vendor: %s", glGetString(GL_VENDOR)); + ImGui::Text("Model: %s", glGetString(GL_RENDERER)); + ImGui::Text("GL Version: %s", glGetString(GL_VERSION)); + ImGui::Text("GLSL Version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + } + ImGui::End(); + + if (ImGui::Begin("Palettes", nullptr, ImGuiWindowFlags_MenuBar)) + { + f32 width = ImGui::GetWindowContentRegionWidth(); + for (auto& pal : gRendererPals) + { + ImGui::Image(GL_TO_IMGUI_TEX(pal.mPalTextureID), ImVec2(width, 16)); + } + } + ImGui::End(); - for (s32 i = 0; i < (1500 / 64); i++) + if (ImGui::Begin("Background Texture", nullptr, ImGuiWindowFlags_MenuBar)) { - ImVec2 pos1Line = ImVec2(pos.x + (i * 64), pos.y); - ImVec2 pos2Line = ImVec2(pos.x + (i * 64), pos.y + 512); - ImGui::GetWindowDrawList()->AddLine(pos1Line, pos2Line, ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 0.2f))); + ImGui::Text("BG Handle: %i", GetBackgroundTexture()); + auto region = ImGui::GetContentRegionAvail(); + auto bgTexId = GetBackgroundTexture(); + ImGui::Image(GL_TO_IMGUI_TEX(bgTexId), region); } + ImGui::End(); + if (ImGui::Begin("FrameBuffer Texture", nullptr, ImGuiWindowFlags_MenuBar)) + { + auto region = ImGui::GetContentRegionAvail(); + ImGui::Image(GL_TO_IMGUI_TEX(mWindowFrameBuffer.texture_handle), region); + } + ImGui::End(); - for (size_t i = 0; i < gRendererTextures.size(); i++) + if (ImGui::Begin("Last Temp Texture", nullptr, ImGuiWindowFlags_MenuBar)) { - ImGui::SetCursorPos(ImVec2(static_cast(gRendererTextures[i].mVramRect.x), static_cast(gRendererTextures[i].mVramRect.y + 50))); - ImVec2 xpos = ImGui::GetCursorScreenPos(); - f32 textureWidth = static_cast(gRendererTextures[i].mVramRect.w); - f32 textureHeight = static_cast(gRendererTextures[i].mVramRect.h); - - ImVec2 size = ImVec2(xpos.x + textureWidth, xpos.y + textureHeight); - ImGui::Image(GL_TO_IMGUI_TEX(gRendererTextures[i].mTextureID), {textureWidth, textureHeight}); - ImGui::GetWindowDrawList()->AddRect(xpos, size, ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 0.3f))); + ImGui::Text("Size: %i, %i", mTempSpriteBuffer.width, mTempSpriteBuffer.height); + auto region = ImGui::GetContentRegionAvail(); + ImGui::Image(GL_TO_IMGUI_TEX(mTempSpriteBuffer.texture_handle), region); } - if (ImGui::IsWindowHovered()) + ImGui::End(); + + if (ImGui::Begin("VRAM", nullptr, ImGuiWindowFlags_MenuBar)) { - ImGui::BeginTooltip(); - ImGui::Text("%d, %d", (s32)(io.MousePos.x - pos.x), (s32)(io.MousePos.y - pos.y)); - ImGui::EndTooltip(); + ImVec2 pos = ImGui::GetWindowPos(); + + for (s32 i = 0; i < (1500 / 64); i++) + { + ImVec2 pos1Line = ImVec2(pos.x + (i * 64), pos.y); + ImVec2 pos2Line = ImVec2(pos.x + (i * 64), pos.y + 512); + ImGui::GetWindowDrawList()->AddLine(pos1Line, pos2Line, ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 0.2f))); + } + + + for (size_t i = 0; i < gRendererTextures.size(); i++) + { + ImGui::SetCursorPos(ImVec2(static_cast(gRendererTextures[i].mVramRect.x), static_cast(gRendererTextures[i].mVramRect.y + 50))); + ImVec2 xpos = ImGui::GetCursorScreenPos(); + f32 textureWidth = static_cast(gRendererTextures[i].mVramRect.w); + f32 textureHeight = static_cast(gRendererTextures[i].mVramRect.h); + + ImVec2 size = ImVec2(xpos.x + textureWidth, xpos.y + textureHeight); + ImGui::Image(GL_TO_IMGUI_TEX(gRendererTextures[i].mTextureID), { textureWidth, textureHeight }); + ImGui::GetWindowDrawList()->AddRect(xpos, size, ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 0.3f))); + } + if (ImGui::IsWindowHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("%d, %d", (s32)(io.MousePos.x - pos.x), (s32)(io.MousePos.y - pos.y)); + ImGui::EndTooltip(); + } } + ImGui::End(); } - ImGui::End(); } void OpenGLRenderer::Destroy() @@ -725,11 +1106,90 @@ void OpenGLRenderer::Destroy() } } +bool CreateRenderBuffer(FrameBuffer* pFrameBuffer, int width, int height, GLint interpolateMode, bool alpha = false) +{ + if (pFrameBuffer->handle == 0) + { + glGenFramebuffers(1, &pFrameBuffer->handle); + glBindFramebuffer(GL_FRAMEBUFFER, pFrameBuffer->handle); + glGenTextures(1, &pFrameBuffer->texture_handle); + glBindTexture(GL_TEXTURE_2D, pFrameBuffer->texture_handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pFrameBuffer->texture_handle, 0); + } + + if (width != pFrameBuffer->width && height != pFrameBuffer->height) + { + glBindTexture(GL_TEXTURE_2D, pFrameBuffer->texture_handle); + glTexImage2D(GL_TEXTURE_2D, 0, (alpha) ? GL_RGBA : GL_RGB, width, height, 0, (alpha) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, interpolateMode); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, interpolateMode); + + pFrameBuffer->width = width; + pFrameBuffer->height = height; + } + + + return true; +} + +void OpenGLRenderer::CreateWindowFrameBuffer(int width, int height) +{ + CreateRenderBuffer(&mWindowFrameBuffer, width, height, GL_NEAREST); +} + +void OpenGLRenderer::RenderFrameBuffer() +{ + //bool keepAspet = false; + + int w, h; + float ratio = 640.0f / 480.0f; + bool keepAspect = true; + + SDL_GetWindowSize(mWindow, &w, &h); + m_View = glm::ortho(0, static_cast(w), static_cast(h), 0, 0, 1); + + // calculate the destination rectangle for the framebuffer using the window size and desired aspect ratio + glm::vec4 destRect = glm::vec4(0, 0, w, h); + + if (keepAspect) + { + if (w > h * ratio) + { + destRect.x = (w - (h * ratio)) / 2; + destRect.z = h * ratio; + } + else + { + destRect.y = (h - (w / ratio)) / 2; + destRect.w = w / ratio; + } + } + + if (gShowAnimIDs) + { + ImDrawList* bgDrawList = ImGui::GetBackgroundDrawList(); + + for (auto& text : gDebugTexts) + { + glm::vec2 pos = { destRect.x + (text.screen_space_x * (destRect.z / 640.0f)), destRect.y + (text.screen_space_y * (destRect.w / 240.0f)) }; + bgDrawList->AddText({ pos.x + 2 ,pos.y + 2 }, IM_COL32(0, 0, 0, 255), text.text.c_str()); + bgDrawList->AddText({ pos.x ,pos.y }, text.color, text.text.c_str()); + } + } + + gDebugTexts.clear(); + + Renderer_SetBlendMode(TPageAbr::eBlend_0); + DrawTexture(mWindowFrameBuffer.texture_handle, destRect.x, destRect.y, destRect.z, destRect.w); +} + bool OpenGLRenderer::Create(TWindowHandleType window) { mWindow = window; mWireframe = false; + gGLInstance = this; + // Find the opengl driver const s32 numDrivers = SDL_GetNumRenderDrivers(); if (numDrivers < 0) @@ -765,7 +1225,7 @@ bool OpenGLRenderer::Create(TWindowHandleType window) } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); // Create context @@ -804,9 +1264,6 @@ bool OpenGLRenderer::Create(TWindowHandleType window) glGenBuffers(1, &mIBO); glGenBuffers(1, &mVBO); - // Set our Projection Matrix, so stuff doesn't get rendered in the quantum realm. - m_View = glm::ortho(0, 640, 240, 0, 0, 1); - //mTextureShader.LoadFromFile("shaders/texture.vsh", "shaders/texture.fsh"); mTextureShader.LoadSource(gShader_TextureVSH, gShader_TextureFSH); return true; @@ -823,42 +1280,46 @@ void OpenGLRenderer::Clear(u8 /*r*/, u8 /*g*/, u8 /*b*/) } t++;*/ - static bool firstFrame = true; - if (!firstFrame) - { - ImGui::Render(); - ImGui::EndFrame(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - } - else + +} + +bool gDebugWindowEnabled = false; + +void OpenGLRenderer::StartFrame(s32 /*xOff*/, s32 /*yOff*/) +{ + static s32 oldWidth = 0; + static s32 oldHeight = 0; + + SDL_GetWindowSize(mWindow, &mWindowWidth, &mWindowHeight); + + if (oldWidth != mWindowWidth || oldHeight != mWindowHeight) { - firstFrame = false; + CreateWindowFrameBuffer(mWindowWidth, mWindowHeight); } + oldWidth = mWindowWidth; + oldHeight = mWindowHeight; + ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(mWindow); ImGui::NewFrame(); - SDL_GL_SwapWindow(mWindow); + //SDL_GL_SwapWindow(mWindow); - DebugWindow(); + if (gDebugWindowEnabled) + { + DebugWindow(); + } glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - s32 wW, wH; - SDL_GetWindowSize(mWindow, &wW, &wH); - glViewport(0, 0, wW, wH); + // Set our Projection Matrix, so stuff doesn't get rendered in the quantum realm. + m_View = glm::ortho(0, 640, 0, 240, 0, 1); - Renderer_SetBlendMode(TPageAbr::eBlend_0); - if (mBackgroundTexture != 0) - { - DrawTexture(mBackgroundTexture, 0, 0, 640, 240); - } -} + glViewport(0, 0, mWindowWidth, mWindowHeight); -void OpenGLRenderer::StartFrame(s32 /*xOff*/, s32 /*yOff*/) -{ + glBindFramebuffer(GL_FRAMEBUFFER, mWindowFrameBuffer.handle); } // This function should free both vrams allocations AND palettes, cause theyre kinda the same thing. @@ -898,6 +1359,21 @@ void OpenGLRenderer::PalSetData(const PalRecord& record, const u8* pPixels) void OpenGLRenderer::EndFrame() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDisable(GL_SCISSOR_TEST); + + RenderFrameBuffer(); + + ImGui::Render(); + ImGui::EndFrame(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +} + +void OpenGLRenderer::Present() +{ + SDL_GL_SwapWindow(mWindow); + + CustomRender_ClearCommands(); } void OpenGLRenderer::BltBackBuffer(const SDL_Rect* /*pCopyRect*/, const SDL_Rect* /*pDst*/) @@ -941,7 +1417,7 @@ void OpenGLRenderer::SetClipDirect(s32 x, s32 y, s32 width, s32 height) glEnable(GL_SCISSOR_TEST); glScissor(static_cast((x / 640.0f) * w), - static_cast(((240 - y - height) / 240.0f) * h), + static_cast((y / 240.0f) * h), static_cast((width / 640.0f) * w), static_cast((height / 240.0f) * h)); } @@ -955,8 +1431,8 @@ void OpenGLRenderer::SetScreenOffset(Prim_ScreenOffset& offset) { m_View = glm::ortho(static_cast(offset.field_C_xoff), static_cast(640 + offset.field_C_xoff), - static_cast(240 + offset.field_E_yoff), - static_cast(offset.field_E_yoff), 0.0f, 1.0f); + static_cast(offset.field_E_yoff), + static_cast(240 + offset.field_E_yoff), 0.0f, 1.0f); } void OpenGLRenderer::Draw(Prim_Sprt& sprt) @@ -965,11 +1441,11 @@ void OpenGLRenderer::Draw(Prim_Sprt& sprt) return; // Detect our magic code and render our cam. - if (sprt.mBase.header.rgb_code.r == 255 && sprt.mBase.header.rgb_code.g == 254 && sprt.mBase.header.rgb_code.b == 253) + /*if (sprt.mBase.header.rgb_code.r == 255 && sprt.mBase.header.rgb_code.g == 254 && sprt.mBase.header.rgb_code.b == 253) { RenderBackground(); return; - } + }*/ PSX_Point vramPoint = Renderer_VRamFromTPage(mLastTPage); s16 textureMode = (mLastTPage >> 7) & 3; @@ -977,10 +1453,11 @@ void OpenGLRenderer::Draw(Prim_Sprt& sprt) // FG1 Blocks if (vramPoint.field_0_x < 640) { - glm::ivec4 lastClip = mLastClip; - SetClipDirect(sprt.mBase.vert.x, sprt.mBase.vert.y, sprt.field_14_w + 1, sprt.field_16_h + 1); - RenderBackground(); - SetClipDirect(lastClip.x, lastClip.y, lastClip.z, lastClip.w); + const glm::vec2 uv0 = glm::vec2(sprt.mBase.vert.x / 640.0f, sprt.mBase.vert.y / 240.0f); + const glm::vec2 uv1 = glm::vec2((sprt.mBase.vert.x + sprt.field_14_w + 1) / 640.0f, (sprt.mBase.vert.y + sprt.field_16_h + 1) / 240.0f); + + DrawTexture(GetBackgroundTexture(), static_cast(sprt.mBase.vert.x), static_cast(sprt.mBase.vert.y), static_cast(sprt.field_14_w + 1), static_cast(sprt.field_16_h + 1), uv0, uv1); + return; } @@ -1052,6 +1529,7 @@ void OpenGLRenderer::Draw(Prim_GasEffect& gasEffect) void OpenGLRenderer::Draw(Prim_Tile& tile) { + if (!gRenderEnable_TILE) return; @@ -1270,6 +1748,123 @@ void OpenGLRenderer::Draw(Poly_F4& poly) mTextureShader.UnUse(); } +void DrawCustomSprite(Poly_FT4& poly, CustomRenderSpriteFormat* sprite) +{ + static bool firstLoad = true; + + if (firstLoad) + { + firstLoad = false; + + LoadAllExternalTextures(); + } + + if (FrameTableOffsetExists(sprite->frametable_offset, gIsGameAE)) + { + AnimRecord animRecord = AnimRecFrameTable(sprite->frametable_offset, sprite->resource_id, gIsGameAE); + + const Uint32 debugColors[] = { + IM_COL32(0, 0xFF, 0, 0xFF), + IM_COL32(0xFF, 0, 0, 0xFF), + IM_COL32(0xFF, 0xC0, 0, 0xFF), + IM_COL32(0xFF, 0xFC, 0, 0xFF), + IM_COL32(0, 0xFF, 0xFF, 0xFF), + }; + + std::string animName = std::string(magic_enum::enum_name(animRecord.mId)); + + if (animRecord.mId != AnimId::ObjectShadow) + { + gDebugTexts.push_back({ animName + " | " + std::to_string(sprite->frametable_offset), sprite->x, sprite->y, debugColors[static_cast(animRecord.mId) % (sizeof(debugColors) / sizeof(Uint32))] }); + } + + // TODO: For now we don't draw custom ObjectShadows till we can properly render them ( direct use of triangle buffer ) + if (animRecord.mId == AnimId::ObjectShadow || !gExternalTexturesEnabled || gLoadedExternalTextures.find(animRecord.mId) == gLoadedExternalTextures.end() || sprite->resource_id != animRecord.mResourceId) { + + CustomRender_RemoveCommand(&poly); + gGLInstance->Draw(poly); + return; + } + + ExternalTextureMeta& loadedTexture = gLoadedExternalTextures[animRecord.mId]; + + if (sprite->frame >= loadedTexture.textures.size()) + { + SetPrimExtraPointerHack(&poly, sprite->origPtr); + gGLInstance->Draw(poly); + return; + } + + auto cache = loadedTexture.textures[sprite->frame]; + + // if a flipped texture exists, then use it instead + const bool flippedTextureExists = sprite->flip && loadedTexture.textures_flipped.find(sprite->frame) != loadedTexture.textures_flipped.end(); + if (flippedTextureExists) + { + cache = loadedTexture.textures_flipped[sprite->frame]; + } + + glm::vec2 imgSize = glm::vec2(loadedTexture.meta.size_width, loadedTexture.meta.size_height) * sprite->scale; + imgSize.y *= 0.5f; + + Renderer_ParseTPageBlendMode(poly.mVerts[0].mUv.tpage_clut_pad); + + float offX = (loadedTexture.meta.offset_x - 1) * sprite->scale; + float offY = (loadedTexture.meta.offset_y - 2) * sprite->scale * 0.5f; + + glm::vec2 uv0 = { 0.0f, 0.0f }; + glm::vec2 uv1 = { 1.0f, 1.0f }; + + if (sprite->flip) + { + offX = (loadedTexture.meta.size_width - (loadedTexture.meta.offset_x - 1)) * sprite->scale; + uv0 = { 1.0f,0.0f }; + uv1 = { 0.0f, 1.0f }; + } + + f32 r = sprite->r / 128.0f; + f32 g = sprite->g / 128.0f; + f32 b = sprite->b / 128.0f; + + // clamp rgb values to 0 - 1 + r = glm::clamp(r, 0.0f, 1.0f); + g = glm::clamp(g, 0.0f, 1.0f); + b = glm::clamp(b, 0.0f, 1.0f); + + //glm::vec3 color = glm::vec3(sprite->r / 128.0f, sprite->g / 128.0f, sprite->b / 128.0f); + glm::vec3 color = glm::vec3(r, g, b); + + gGLInstance->DrawTexture(cache.handle, (f32)sprite->x - offX, (f32)sprite->y - offY, imgSize.x, imgSize.y, color, uv0, uv1); + + // if we have an emissive texture, draw it + if (loadedTexture.textures_emissive.find(sprite->frame) != loadedTexture.textures_emissive.end()) + { + auto emissiveTexture = loadedTexture.textures_emissive[sprite->frame]; + + if (flippedTextureExists && loadedTexture.textures_flipped_emissive.find(sprite->frame) != loadedTexture.textures_flipped_emissive.end()) + { + emissiveTexture = loadedTexture.textures_flipped_emissive[sprite->frame]; + } + + Renderer_SetBlendMode(TPageAbr::eBlend_1); + + gGLInstance->DrawTexture(emissiveTexture.handle, (f32)sprite->x - offX, (f32)sprite->y - offY, imgSize.x, imgSize.y, glm::vec3(1,1,1) - color, uv0, uv1); + } + } +} + +glm::vec4 calc_bounds(glm::vec2* points, int numPoints) { + glm::vec4 bounds = glm::vec4(points[0], points[0]); + for (int i = 1; i < numPoints; i++) { + bounds.x = glm::min(bounds.x, points[i].x); + bounds.y = glm::min(bounds.y, points[i].y); + bounds.z = glm::max(bounds.z, points[i].x); + bounds.w = glm::max(bounds.w, points[i].y); + } + return bounds; +} + +/* // Slow experimental pre rendering of paletted sprites to get linear filtering. void OpenGLRenderer::Draw(Poly_FT4& poly) { if (!gRenderEnable_FT4) @@ -1281,19 +1876,219 @@ void OpenGLRenderer::Draw(Poly_FT4& poly) TextureCache* pTexture = nullptr; // Some polys have their texture data directly attached to polys. - if (GetPrimExtraPointerHack(&poly)) - pTexture = Renderer_TextureFromAnim(poly); + auto ptrData = GetPrimExtraPointerHack(&poly); + if (ptrData) + { + if (*(int*)ptrData == 0x12345678) + { + // HD Hack + DrawCustomSprite(poly, (CustomRenderSpriteFormat*)ptrData); + return; + } + else + { + pTexture = Renderer_TextureFromAnim(poly); + } + } else pTexture = Renderer_TexFromTPage(poly.mVerts[0].mUv.tpage_clut_pad, poly.mUv.u, poly.mUv.v); PaletteCache* pPal = Renderer_ClutToPalette(poly.mUv.tpage_clut_pad); + int palDepth = 0; + + if (pPal != nullptr) + { + if (pTexture->mPalNormMulti != 0) + palDepth = pPal->mPalDepth * gFakeTextureCache.mPalNormMulti; + else + palDepth = pPal->mPalDepth; + } + if (pTexture == nullptr) { //LOG_WARNING("Trying to render FT4 with no texture!"); return; } + const GLuint indexData[6] = { 1, 0, 3, 3, 0, 2 }; + + f32 r = poly.mBase.header.rgb_code.r / 64.0f; + f32 g = poly.mBase.header.rgb_code.g / 64.0f; + f32 b = poly.mBase.header.rgb_code.b / 64.0f; + + if (pTexture->mIgnoreColor) + { + r = 1.0f; + g = 1.0f; + b = 1.0f; + } + + s32 xOff = (pTexture->mVramRect.x & 63); + s32 bppMulti = 1; + + switch (pTexture->mBitDepth) + { + case BitDepth::e8Bit: + bppMulti = 2; + break; + case BitDepth::e4Bit: + bppMulti = 4; + break; + default: + break; + } + + xOff *= bppMulti; + + // macros suck. todo: fix that +#define UV_U(v) (f32)(((pTexture->mUvOffset.field_0_x + v) - xOff) / (f32)(pTexture->mVramRect.w * bppMulti)) +#define UV_V(v) (f32)(((pTexture->mUvOffset.field_2_y + v) - static_cast(pTexture->mVramRect.y)) / (f32) pTexture->mVramRect.h) + + VertexData verts[4] = { + {(f32)poly.mBase.vert.x, (f32)poly.mBase.vert.y, 0, r, g, b, UV_U(poly.mUv.u), UV_V(poly.mUv.v)}, + {(f32)poly.mVerts[0].mVert.x, (f32)poly.mVerts[0].mVert.y, 0, r, g, b, UV_U(poly.mVerts[0].mUv.u), UV_V(poly.mVerts[0].mUv.v)}, + {(f32)poly.mVerts[1].mVert.x, (f32)poly.mVerts[1].mVert.y, 0, r, g, b, UV_U(poly.mVerts[1].mUv.u), UV_V(poly.mVerts[1].mUv.v)}, + {(f32)poly.mVerts[2].mVert.x, (f32)poly.mVerts[2].mVert.y, 0, r, g, b, UV_U(poly.mVerts[2].mUv.u), UV_V(poly.mVerts[2].mUv.v)} }; + + // Todo: Calculate the bounds of the polygon, render to a temp frame buffer, then re render at full scale. + // This lets us render fast palettes using hardware acceleration then be able to stretch using a linear filter. + // Cause currently using linear interp screws up our shader (cause pixels are actually palette index, lerping between palette indexes + // causes bad artifacting. + + std::vector points; + for (auto p : verts) + { + points.push_back({ p.x, p.y }); + } + + glm::vec4 bounds = calc_bounds(points.data(), static_cast(points.size())); + + + float width = bounds.z - bounds.x; + float height = bounds.w - bounds.y; + + // Start rendering into temp buffer + CreateRenderBuffer(&mTempSpriteBuffer, 256, 256, GL_LINEAR, true); + glBindFramebuffer(GL_FRAMEBUFFER, mTempSpriteBuffer.handle); + glViewport(0, 0, mTempSpriteBuffer.width, mTempSpriteBuffer.height); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Render the texture using the palette + auto oldMView = m_View; + m_View = glm::ortho(0, static_cast(mTempSpriteBuffer.width), 0, static_cast(mTempSpriteBuffer.height), 0, 1); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + DrawTexturePalette(pTexture->mTextureID, 0,0, mTempSpriteBuffer.width, mTempSpriteBuffer.height, glm::vec4(1,1,1,1), glm::vec2(0, 0), glm::vec2(1, 1), pPal, palDepth); + m_View = oldMView; + // Revert back to screen buffer + glBindFramebuffer(GL_FRAMEBUFFER, mWindowFrameBuffer.handle); + glViewport(0, 0, mWindowWidth, mWindowHeight); + + mTextureShader.Use(); + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mTempSpriteBuffer.texture_handle); + //Renderer_BindTexture(pTexture); + + if (pTexture->mIsFG1) + { + const f32 overdraw = 0.2f; // stops weird line rendering issues. + // This is an FG1, so UV's are maxed; + verts[0] = { (f32)poly.mBase.vert.x, (f32)poly.mBase.vert.y, 0, 1.0f, 1.0f, 1.0f, 0, 0 }; + verts[1] = { (f32)poly.mVerts[0].mVert.x + overdraw, (f32)poly.mVerts[0].mVert.y, 0, 1.0f, 1.0f, 1.0f, 1, 0 }; + verts[2] = { (f32)poly.mVerts[1].mVert.x, (f32)poly.mVerts[1].mVert.y + overdraw, 0, 1.0f, 1.0f, 1.0f, 0, 1 }; + verts[3] = { (f32)poly.mVerts[2].mVert.x + overdraw, (f32)poly.mVerts[2].mVert.y + overdraw, 0, 1.0f, 1.0f, 1.0f, 1, 1 }; + + + // Hack, set palette texture to our background. + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, GetBackgroundTexture()); + + mTextureShader.UniformVec4("m_FG1Size", glm::vec4(poly.mBase.vert.x, poly.mBase.vert.y, pTexture->mVramRect.w + overdraw, pTexture->mVramRect.h + overdraw)); + mTextureShader.Uniform1i("m_FG1", true); + } + + // Set our Projection Matrix, so stuff doesn't get rendered in the quantum realm. + mTextureShader.UniformMatrix4fv("m_MVP", GetMVP()); + + mTextureShader.Uniform1i("m_Sprite", 0); // Set m_Sprite to GL_TEXTURE0 + mTextureShader.Uniform1i("m_Palette", 1); // Set m_Palette to GL_TEXTURE1 + mTextureShader.Uniform1i("m_Textured", true); + mTextureShader.Uniform1i("m_PaletteEnabled", false); + mTextureShader.Uniform1i("m_PaletteDepth", 0); + + // Hack to use a HD menu font. + if (pTexture->mVramRect.w == 64 && pTexture->mVramRect.h == 256) + { + static GLuint FontTexture = TextureFromFile("menufont.png"); + + if (FontTexture != 0) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, FontTexture); + mTextureShader.Uniform1i("m_PaletteEnabled", false); + } + } + + + //for (auto& p : verts) + //{ + // p.x -= bounds.x; + // p.y -= bounds.y; + //} + + + + Renderer_ParseTPageBlendMode(poly.mVerts[0].mUv.tpage_clut_pad); + DrawTriangles(verts, 4, indexData, 6); + //static GLuint testText = TextureFromFile("menufont.png"); + //DrawTexture(testText, bounds.x, bounds.y, width, height); + + mTextureShader.Uniform1i("m_FG1", false); + // render temp frame buffer + + mTextureShader.UnUse(); +} +*/ + + +void OpenGLRenderer::Draw(Poly_FT4& poly) +{ + if (!gRenderEnable_FT4) + return; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + TextureCache* pTexture = nullptr; + + auto customCommand = CustomRender_GetCommand(&poly); + + if (customCommand != nullptr) + { + DrawCustomSprite(poly, customCommand); + return; + } + + // Some polys have their texture data directly attached to polys. + auto ptrData = GetPrimExtraPointerHack(&poly); + if (ptrData) + { + pTexture = Renderer_TextureFromAnim(poly); + } + else + pTexture = Renderer_TexFromTPage(poly.mVerts[0].mUv.tpage_clut_pad, poly.mUv.u, poly.mUv.v); + + PaletteCache* pPal = Renderer_ClutToPalette(poly.mUv.tpage_clut_pad); + + if (pTexture == nullptr) + { + LOG_WARNING("Trying to render FT4 with no texture!"); + return; + } + mTextureShader.Use(); f32 r = poly.mBase.header.rgb_code.r / 64.0f; @@ -1480,17 +2275,6 @@ void OpenGLRenderer::Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* p TextureCache* tc = Renderer_TexFromVRam(rect); tc->mVramRect = rect; - if (ImGui::Begin("VRAM", nullptr, ImGuiWindowFlags_MenuBar)) - { - ImGui::SetCursorPos(ImVec2(static_cast(tc->mVramRect.x), static_cast(tc->mVramRect.y + 50))); - f32 textureWidth = static_cast(tc->mVramRect.w); - f32 textureHeight = static_cast(tc->mVramRect.h); - ImVec2 xpos = ImGui::GetCursorScreenPos(); - ImVec2 size = ImVec2(xpos.x + textureWidth, xpos.y + textureHeight); - ImGui::GetWindowDrawList()->AddRect(xpos, size, ImGui::GetColorU32(ImVec4(0.0f, 1.0f, 0.0f, 1.0f))); - } - ImGui::End(); - glBindTexture(GL_TEXTURE_2D, tc->mTextureID); bool aoFG1 = true; @@ -1521,10 +2305,10 @@ void OpenGLRenderer::Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* p if (rect.x == 624) { - if (mBackgroundTexture == 0) - mBackgroundTexture = Renderer_CreateTexture(); + if (mStitchedBackground == 0) + mStitchedBackground = Renderer_CreateTexture(GL_NEAREST); - glBindTexture(GL_TEXTURE_2D, mBackgroundTexture); + glBindTexture(GL_TEXTURE_2D, mStitchedBackground); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, 640, 240, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, gDecodeBuffer); } } @@ -1544,48 +2328,72 @@ void OpenGLRenderer::Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* p } } -void HackSetBackground(const char_type* path) +void OpenGLRenderer::LoadExternalCam(const char* path, const unsigned char* key, int keyLength) { - //return; + std::vector fileData; + bool encrypted = true; - const char_type* camSearchs[] = { - "hd/%s.PNG", - "hd/%s.CAM.PNG", - "hd/%s.CAM.cam.PNG"}; + // Try to keep all paths and filenames lowercase for our linux friends. + std::ifstream file(gExternalAssetPath + "cams/"s + std::string(path).substr(0, 8) + ".cam2", std::ios::binary); - FILE* fh = NULL; + if (file.is_open()) + { + fileData = std::vector((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + } - for (s32 i = 0; i < 3; i++) + if (fileData.size() == 0) { - char_type newPath[100]; - char_type camHack[9] = {}; - memcpy(camHack, path, 8); - sprintf(newPath, camSearchs[i], camHack); - fh = fopen(newPath, "rb"); + std::ifstream fileRaw(gExternalAssetPath + "cams/"s + std::string(path).substr(0, 8) + ".png", std::ios::binary); - if (fh != NULL) + if (fileRaw.is_open()) { - break; + fileData = std::vector((std::istreambuf_iterator(fileRaw)), std::istreambuf_iterator()); + fileRaw.close(); + encrypted = false; + } + else + { + glDeleteTextures(1, &mHDBackgroundTexture); + mHDBackgroundTexture = 0; + return; } } - if (fh == NULL) + BYTE* fPtr = fileData.data(); + + if (encrypted) { - /*glDeleteTextures(1, &mBackgroundTexture); - mBackgroundTexture = 0;*/ - return; + // XOR the custom cam file with the data from the original game. + // You wouldn't steal an Abe? https://www.youtube.com/watch?v=HmZm8vNHBSU + for (unsigned int i = 0; i < fileData.size(); i++) + { + fPtr[i] ^= key[i % keyLength]; + } } - s32 x = 0, y = 0; - s32 comp = 0; - const u8* data = stbi_load_from_file(fh, &x, &y, &comp, 4); + int x = 0, y = 0, comp = 0; + const unsigned char* data = stbi_load_from_memory(fileData.data(), static_cast(fileData.size()), &x, &y, &comp, 4); + + if (comp == 0) + { + // failed to load image + std::string message; + message.resize(1000); + sprintf(message.data(), "%s corrupted! Make sure you're using the correct .lvl files for decryption!", path); + Alive_Show_ErrorMsg(message.data()); + glDeleteTextures(1, &mHDBackgroundTexture); + mHDBackgroundTexture = 0; + return; + } - if (mBackgroundTexture == 0) + // Check if we've created a texture handle already, if not, do so. + if (mHDBackgroundTexture == 0) { - glGenTextures(1, &mBackgroundTexture); + glGenTextures(1, &mHDBackgroundTexture); } - glBindTexture(GL_TEXTURE_2D, mBackgroundTexture); + glBindTexture(GL_TEXTURE_2D, mHDBackgroundTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); @@ -1594,7 +2402,5 @@ void HackSetBackground(const char_type* path) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - stbi_image_free((void*) data); - - fclose(fh); -} + stbi_image_free((void*)data); +} \ No newline at end of file diff --git a/Source/AliveLibAE/Renderer/OpenGLRenderer.hpp b/Source/AliveLibAE/Renderer/OpenGLRenderer.hpp index 844f443ba..aa79858da 100644 --- a/Source/AliveLibAE/Renderer/OpenGLRenderer.hpp +++ b/Source/AliveLibAE/Renderer/OpenGLRenderer.hpp @@ -58,6 +58,7 @@ class OpenGLRenderer final : public IRenderer void Clear(u8 r, u8 g, u8 b) override; void StartFrame(s32 xOff, s32 yOff) override; void EndFrame() override; + void Present() override; void BltBackBuffer(const SDL_Rect* pCopyRect, const SDL_Rect* pDst) override; void OutputSize(s32* w, s32* h) override; bool UpdateBackBuffer(const void* pPixels, s32 pitch) override; @@ -82,12 +83,15 @@ class OpenGLRenderer final : public IRenderer void Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* pPixels) override; -private: + void LoadExternalCam(const char* path, const unsigned char* key, int keyLength) override; + SDL_Window* mWindow = nullptr; SDL_GLContext mContext = nullptr; GLShader mTextureShader = {}; u16 mLastTPage = 0; bool mWireframe = false; + s32 mWindowWidth = 0; + s32 mWindowHeight = 0; glm::mat4 m_View = {}; @@ -104,12 +108,14 @@ class OpenGLRenderer final : public IRenderer void DebugWindow(); + void CreateWindowFrameBuffer(int width, int height); + void RenderFrameBuffer(); void InitAttributes(); - void DrawTexture(GLuint pTexture, f32 x, f32 y, f32 width, f32 height); + void DrawTexturePalette(GLuint pTexture, f32 x, f32 y, f32 width, f32 height, glm::vec3 color, glm::vec2 uv0, glm::vec2 uv1, PaletteCache* palette, int palDepth); + void DrawTexture(GLuint pTexture, f32 x, f32 y, f32 width, f32 height, glm::vec2 uv0 = glm::vec2(0, 0), glm::vec2 uv1 = glm::vec2(1, 1)); + void DrawTexture(GLuint pTexture, f32 x, f32 y, f32 width, f32 height, glm::vec3 color, glm::vec2 uv0 = glm::vec2(0, 0), glm::vec2 uv1 = glm::vec2(1, 1)); void DrawTriangles(const VertexData* pVertData, s32 vertSize, const GLuint* pIndData, s32 indSize); void DrawLines(const VertexData* pVertData, s32 vertSize, const GLuint* pIndData, s32 indSize); void RenderBackground(); -}; - -void HackSetBackground(const char_type* path); +}; \ No newline at end of file diff --git a/Source/AliveLibAE/Renderer/SoftwareRenderer.cpp b/Source/AliveLibAE/Renderer/SoftwareRenderer.cpp index 91ae3f863..e5f04b541 100644 --- a/Source/AliveLibAE/Renderer/SoftwareRenderer.cpp +++ b/Source/AliveLibAE/Renderer/SoftwareRenderer.cpp @@ -59,12 +59,16 @@ void SoftwareRenderer::PalSetData(const IRenderer::PalRecord& record, const u8* PSX_LoadImage16_4F5E20(&rect, pPixels); } -void SoftwareRenderer::EndFrame() +void SoftwareRenderer::Present() { SDL_RenderPresent(mRenderer); mFrameStarted = false; } +void SoftwareRenderer::EndFrame() +{ +} + void SoftwareRenderer::BltBackBuffer(const SDL_Rect* pCopyRect, const SDL_Rect* pDst) { SDL_RenderCopy(mRenderer, mBackBufferTexture, pCopyRect, pDst); @@ -231,3 +235,8 @@ void SoftwareRenderer::Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* break; } } + +void SoftwareRenderer::LoadExternalCam(const char* /*path*/, const unsigned char* /*key*/, int /*keyLength*/) +{ + LOG_WARNING("LoadExternalCam not implemented for Software Renderer - external cam not loaded."); +} \ No newline at end of file diff --git a/Source/AliveLibAE/Renderer/SoftwareRenderer.hpp b/Source/AliveLibAE/Renderer/SoftwareRenderer.hpp index 6b71ccd92..0489a20b5 100644 --- a/Source/AliveLibAE/Renderer/SoftwareRenderer.hpp +++ b/Source/AliveLibAE/Renderer/SoftwareRenderer.hpp @@ -10,6 +10,7 @@ class SoftwareRenderer : public IRenderer void Clear(u8 r, u8 g, u8 b) override; void StartFrame(s32 xOff, s32 yOff) override; void EndFrame() override; + void Present() override; void BltBackBuffer(const SDL_Rect* pCopyRect, const SDL_Rect* pDst) override; void OutputSize(s32* w, s32* h) override; bool UpdateBackBuffer(const void* pPixels, s32 pitch) override; @@ -34,6 +35,8 @@ class SoftwareRenderer : public IRenderer void Upload(BitDepth bitDepth, const PSX_RECT& rect, const u8* pPixels) override; + void LoadExternalCam(const char* path, const unsigned char* key, int keyLength) override; + private: bool mFrameStarted = false; diff --git a/Source/AliveLibAE/Rope.cpp b/Source/AliveLibAE/Rope.cpp index 7ec1816be..57c3f92f2 100644 --- a/Source/AliveLibAE/Rope.cpp +++ b/Source/AliveLibAE/Rope.cpp @@ -55,7 +55,7 @@ Rope* Rope::ctor_4A0A70(u16 left, s16 top, u16 bottom, FP scale) SetType(AETypes::eLiftRope_108); - const AnimRecord& rec = AnimRec(AnimId::Lift_Rope); + const AnimRecord& rec = AnimRec(AnimId::AE_Rope); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1, 1); SetTint_425600(kRopeTints_55FD68, gMap_5C3030.field_0_current_level); diff --git a/Source/AliveLibAE/ScreenManager.cpp b/Source/AliveLibAE/ScreenManager.cpp index 61f7df7b5..e258aa417 100644 --- a/Source/AliveLibAE/ScreenManager.cpp +++ b/Source/AliveLibAE/ScreenManager.cpp @@ -8,6 +8,10 @@ #include "VRam.hpp" #include "Psx.hpp" #include "Renderer/IRenderer.hpp" +#include "Map.hpp" +#include "PathData.hpp" +#include "Renderer/IRenderer.hpp" +#include "Renderer/OpenGLRenderer.hpp" #include "../AliveLibCommon/CamDecompressor.hpp" ALIVE_VAR(1, 0x5BB5F4, ScreenManager*, pScreenManager_5BB5F4, nullptr); @@ -182,6 +186,15 @@ void ScreenManager::DecompressCameraToVRam_40EF60(u16** ppBits) UnsetDirtyBits_40EDE0(1); UnsetDirtyBits_40EDE0(2); UnsetDirtyBits_40EDE0(3); + + char camName[32] = {}; + Path_Format_CameraName_460FB0( + camName, + gMap_5C3030.field_0_current_level, + gMap_5C3030.field_2_current_path, + gMap_5C3030.field_4_current_camera); + + IRenderer::GetRenderer()->LoadExternalCam(camName, reinterpret_cast(*ppBits), 512); } ScreenManager* ScreenManager::ctor_40E3E0(u8** ppBits, FP_Point* pCameraOffset) @@ -322,15 +335,6 @@ void ScreenManager::VRender_40E6E0(PrimHeader** ppOt) return; } -#if RENDERER_OPENGL - // TODO: A custom sprite prim with magic numbers - // to trigger proper order rendering of our cam. - static Prim_Sprt MagicBackgroundPrim; - Sprt_Init_4F8910(&MagicBackgroundPrim); - SetRGB0(&MagicBackgroundPrim, 255, 254, 253); - OrderingTable_Add_4F8AA0(OtLayer(ppOt, Layer::eLayer_1), &MagicBackgroundPrim.mBase.header); -#endif - PSX_DrawSync_4F6280(0); pCurrent_SprtTPage_5BB5DC = nullptr; sCurrentYPos_5BB5F0 = -1; diff --git a/Source/AliveLibAE/Slig.cpp b/Source/AliveLibAE/Slig.cpp index b9f1e2628..99c75e315 100644 --- a/Source/AliveLibAE/Slig.cpp +++ b/Source/AliveLibAE/Slig.cpp @@ -33,6 +33,8 @@ #include "AmbientSound.hpp" #include "VRam.hpp" #include "Electrocute.hpp" +#include "ExternalAssets.hpp" +#include "PsxDisplay.hpp" const SfxDefinition kSfxInfoTable_5607E0[17] = { {0u, 1u, 58u, 40u, -256, -256}, @@ -735,6 +737,30 @@ void renderWithGlowingEyes(PrimHeader** ot, BaseAliveGameObject* actor, s16* pPa 0, 0); + // Setting emissive to true to enable glowing eyes in HD assets. + auto customRenderCommand = CustomRender_GetCommand(&actor->field_20_animation.field_2C_ot_data[gPsxDisplay_5C1130.field_C_buffer_index]); + + if (customRenderCommand != nullptr) + { + customRenderCommand->emissive = true; + + // restore shadows for sligs + s16 rCol = actor->field_D0_r; + s16 gCol = actor->field_D2_g; + s16 bCol = actor->field_D4_b; + ShadowZone::ShadowZones_Calculate_Colour_463CE0( + FP_GetExponent(actor->field_B8_xpos), + (boundingRect.h + boundingRect.y) / 2, + actor->field_D6_scale, + &rCol, + &gCol, + &bCol); + + customRenderCommand->r = static_cast(rCol); + customRenderCommand->g = static_cast(gCol); + customRenderCommand->b = static_cast(bCol); + } + PSX_RECT rectToInvalidate = {}; actor->field_20_animation.Get_Frame_Rect_409E10(&rectToInvalidate); pScreenManager_5BB5F4->InvalidateRect_40EC90( diff --git a/Source/AliveLibAE/Spark.cpp b/Source/AliveLibAE/Spark.cpp index 25d7f889a..daff33e4b 100644 --- a/Source/AliveLibAE/Spark.cpp +++ b/Source/AliveLibAE/Spark.cpp @@ -90,7 +90,7 @@ Spark* Spark::ctor_4CBBB0(FP xpos, FP ypos, FP scale, u8 count, s16 minAngle, s1 else { // Normal drill type sparks - const AnimRecord& rec = AnimRec(AnimId::Zap_Sparks); + const AnimRecord& rec = AnimRec(AnimId::ChantOrb_Particle_Small); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); auto pParticle = ae_new(); if (pParticle) diff --git a/Source/AliveLibAE/Sparks.cpp b/Source/AliveLibAE/Sparks.cpp index 8e2a717fc..7380b878d 100644 --- a/Source/AliveLibAE/Sparks.cpp +++ b/Source/AliveLibAE/Sparks.cpp @@ -9,7 +9,7 @@ Sparks* Sparks::ctor_416390(FP xpos, FP ypos, FP scale) SetVTable(this, 0x544534); SetType(AETypes::eSparks_22); - const AnimRecord& rec = AnimRec(AnimId::Sparks); + const AnimRecord& rec = AnimRec(AnimId::AE_ZapSpark); u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId); Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1, 1); @@ -57,7 +57,7 @@ void Sparks::vUpdate_416570() if (field_FA_16_random == 0) { - const AnimRecord& animRec = AnimRec(AnimId::Sparks); + const AnimRecord& animRec = AnimRec(AnimId::AE_ZapSpark); field_20_animation.Set_Animation_Data_409C80(animRec.mFrameTableOffset, nullptr); field_FA_16_random = -1; } diff --git a/Source/AliveLibAE/Sys.cpp b/Source/AliveLibAE/Sys.cpp index 9e938fa29..e0735091f 100644 --- a/Source/AliveLibAE/Sys.cpp +++ b/Source/AliveLibAE/Sys.cpp @@ -19,6 +19,10 @@ #include #endif +#if RENDERER_OPENGL +#include "Renderer/OpenGLRenderer.hpp" +#endif + ALIVE_VAR(1, 0xBBBA00, Bool32, sAppIsActivated_BBBA00, FALSE); ALIVE_VAR(1, 0xBBB9F4, TWindowHandleType, sHwnd_BBB9F4, nullptr); #if _WIN32 @@ -828,6 +832,9 @@ static s32 sdl_key_to_win32_vkey(SDL_Scancode key) static bool bNeedToQuit = false; +extern bool gDebugWindowEnabled; +extern bool gExternalTexturesEnabled; + static s32 Sys_EventFilter(void* /*userData*/, SDL_Event* event) { if (event->type == SDL_KEYDOWN) @@ -894,7 +901,15 @@ static s32 Sys_EventFilter(void* /*userData*/, SDL_Event* event) Input_SetKeyState_4EDD80(vk, 1); - if (vk == VK_F5) + if (vk == VK_F1) + { + gDebugWindowEnabled = !gDebugWindowEnabled; + } + else if (vk == VK_F2) + { + gExternalTexturesEnabled = !gExternalTexturesEnabled; + } + else if (vk == VK_F5) { LOG_INFO("Save next frame for " << VK_F5); sQuicksave_SaveNextFrame_5CA4D8 = 1; @@ -1050,6 +1065,10 @@ EXPORT s8 CC Sys_PumpMessages_4EE4F4() } } #endif + +#if RENDERER_OPENGL + ImGui_ImplSDL2_ProcessEvent(&event); +#endif } if (bNeedToQuit) diff --git a/Source/AliveLibAE/VGA.cpp b/Source/AliveLibAE/VGA.cpp index 107ca0b14..85aa88129 100644 --- a/Source/AliveLibAE/VGA.cpp +++ b/Source/AliveLibAE/VGA.cpp @@ -204,7 +204,7 @@ EXPORT void CC VGA_CopyToFront_4F3730(Bitmap* pBmp, RECT* pRect, s32 /*screenMod gTouchController->Render(); } #endif - IRenderer::GetRenderer()->EndFrame(); + IRenderer::GetRenderer()->Present(); } EXPORT void CC VGA_CopyToFront_4F3710(Bitmap* pBmp, RECT* pRect) diff --git a/Source/AliveLibAO/Abe.cpp b/Source/AliveLibAO/Abe.cpp index 7e93e27a4..30588381b 100644 --- a/Source/AliveLibAO/Abe.cpp +++ b/Source/AliveLibAO/Abe.cpp @@ -259,7 +259,7 @@ const AnimId sAbeFrameOffsetTable_4C61A0[166] = { AnimId::Mudokon_AO_M_15_Null, AnimId::Mudokon_HoistBegin, AnimId::Mudokon_HoistIdle, - AnimId::Mudokon_AO_Unknown4, // LandSoft but different from regular mudokon? + AnimId::Mudokon_LandSoft, AnimId::Mudokon_CrouchIdle, AnimId::Mudokon_CrouchToStand, AnimId::Mudokon_StandToCrouch, @@ -327,20 +327,20 @@ const AnimId sAbeFrameOffsetTable_4C61A0[166] = { AnimId::Mudokon_WellBegin, AnimId::Mudokon_Well_Idle, AnimId::Mudokon_Well_Idle, - AnimId::Mudokon_AO_Unknown3, // FallLandDie but AnimId already used?? or did i mess up the order - AnimId::Mudokon_AO_Unknown2, // to fall - AnimId::Mudokon_HandstoneBegin, + AnimId::Mudokon_FallLandDie, + AnimId::Mudokon_Fall, + AnimId::Mudokon_HandstoneBegin, AnimId::Mudokon_HandstoneEnd, AnimId::Mudokon_GrenadeMachineUse, - AnimId::Mudokon_Fall, - AnimId::Mudokon_Fall, + AnimId::Mudokon_FallingFromGrab, + AnimId::Mudokon_FallingFromGrab, AnimId::Mudokon_WalkOffEdge, AnimId::Mudokon_RunOffEdge, AnimId::Mudokon_SneakOffEdge, AnimId::Mudokon_HopToFall, AnimId::Mudokon_RunJumpToFall, - AnimId::Mudokon_LandSoft, - AnimId::Mudokon_AO_Unknown1, // ledge hoist up + AnimId::Mudokon_AO_LandSoft_Long, + AnimId::Mudokon_AO_HoistBegin_Long, AnimId::Mudokon_RollOffEdge, AnimId::Mudokon_LeverUse, AnimId::Mudokon_ElumWalkLoop, @@ -369,11 +369,11 @@ const AnimId sAbeFrameOffsetTable_4C61A0[166] = { AnimId::Mudokon_ElumMidWalkEnd, AnimId::Mudokon_ElumBeesStruggling, AnimId::Mudokon_SlapBomb, - AnimId::Mudokon_FallLandDie, // knock forward? + AnimId::Mudokon_KnockForward, AnimId::Mudokon_RollingKnockForward, - AnimId::Mudokon_Idle, // knock forward get up? - AnimId::Mudokon_AO_Null, // lift use up - AnimId::Mudokon_AO_LiftUse, // lift use down + AnimId::Mudokon_Idle, + AnimId::Mudokon_LiftUseUp, + AnimId::Mudokon_LiftUseDown, AnimId::Mudokon_LiftGrabBegin, AnimId::Mudokon_LiftGrabEnd, AnimId::Mudokon_LiftGrabIdle, @@ -403,10 +403,10 @@ const AnimId sAbeFrameOffsetTable_4C61A0[166] = { AnimId::Mudokon_Idle_RubEyes, AnimId::Mudokon_Idle_StretchArms, AnimId::Mudokon_Idle_Yawn, - AnimId::Mudokon_AO_ToShrykull, - AnimId::Mudokon_AO_ShrykullEnd, + AnimId::Mudokon_ToShrykull, + AnimId::Mudokon_EndShrykull, AnimId::Mudokon_PoisonGasDeath, - AnimId::None}; + AnimId::None }; /*const s32 sAbeFrameOffsetTable_4C61A0[166] = { 55968, diff --git a/Source/AliveLibAO/Animation.cpp b/Source/AliveLibAO/Animation.cpp index 45b65757e..b46d7511b 100644 --- a/Source/AliveLibAO/Animation.cpp +++ b/Source/AliveLibAO/Animation.cpp @@ -24,6 +24,7 @@ #include "Blood.hpp" #include "Renderer/IRenderer.hpp" #include "AnimResources.hpp" +#include "ExternalAssets.hpp" // Fix pollution from windows.h #undef min @@ -623,6 +624,27 @@ void Animation::VRender_403AE0(s32 xpos, s32 ypos, PrimHeader** ppOt, s16 width, SetXY3(pPoly, width_adjusted, height_adjusted); SetPrimExtraPointerHack(pPoly, nullptr); + + + ////////////// + // HD HAX + ////////////// + + CustomRenderSpriteFormat sprite; + + sprite.x = PsxToPCX(xpos); + sprite.y = ypos; + sprite.frame = (this->field_92_current_frame == -1) ? 0 : this->field_92_current_frame; + sprite.frametable_offset = field_18_frame_table_offset; + sprite.resource_id = ResourceManager::Get_Header_455620(field_20_ppBlock)->field_C_id; + sprite.r = pPoly->mBase.header.rgb_code.r; + sprite.g = pPoly->mBase.header.rgb_code.g; + sprite.b = pPoly->mBase.header.rgb_code.b; + sprite.scale = (float)FP_GetDouble(this->field_14_scale); + sprite.flip = this->field_4_flags.Get(AnimFlags::eBit5_FlipX); + + CustomRender_AddCommand(pPoly, sprite); + OrderingTable_Add_498A80(OtLayer(ppOt, field_C_layer), &pPoly->mBase.header); } } diff --git a/Source/AliveLibAO/BaseBomb.cpp b/Source/AliveLibAO/BaseBomb.cpp index 1739e54d1..554462a34 100644 --- a/Source/AliveLibAO/BaseBomb.cpp +++ b/Source/AliveLibAO/BaseBomb.cpp @@ -114,7 +114,7 @@ void BaseBomb::VUpdate_417580() if (field_10_anim.field_92_current_frame == 3) { - const AnimRecord& rec = AO::AnimRec(AnimId::Explosion_Mine); + const AnimRecord& rec = AO::AnimRec(AnimId::GroundExplosion); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); if (ppRes) { @@ -231,7 +231,7 @@ BaseBomb* BaseBomb::ctor_4173A0(FP xpos, FP ypos, s32 /*unused*/, FP scale) SetVTable(this, 0x4BAA00); field_4_typeId = Types::eBaseBomb_30; - const AnimRecord rec = AO::AnimRec(AnimId::Explosion_Mine); + const AnimRecord rec = AO::AnimRec(AnimId::GroundExplosion); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); Animation_Init_417FD0(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1); diff --git a/Source/AliveLibAO/Blood.cpp b/Source/AliveLibAO/Blood.cpp index a8078d71c..82ab80b09 100644 --- a/Source/AliveLibAO/Blood.cpp +++ b/Source/AliveLibAO/Blood.cpp @@ -27,7 +27,7 @@ Blood* Blood::ctor_4072B0(FP xpos, FP ypos, FP xOff, FP yOff, FP scale, s16 coun field_BC_sprite_scale = scale; - const AnimRecord rec = AO::AnimRec(AnimId::Blood); + const AnimRecord rec = AO::AnimRec(AnimId::BloodDrop); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); Animation_Init_417FD0(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1); diff --git a/Source/AliveLibAO/DoorFlame.cpp b/Source/AliveLibAO/DoorFlame.cpp index 71b3bc8d6..6ab9e9f09 100644 --- a/Source/AliveLibAO/DoorFlame.cpp +++ b/Source/AliveLibAO/DoorFlame.cpp @@ -174,7 +174,7 @@ class FlameSparks final : public BaseAnimatedWithPhysicsGameObject SetVTable(this, 0x4BB368); field_4_typeId = Types::eNone_0; - const AnimRecord rec = AO::AnimRec(AnimId::Zap_Sparks); + const AnimRecord rec = AO::AnimRec(AnimId::ChantOrb_Particle_Small); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); Animation_Init_417FD0(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1); field_10_anim.field_4_flags.Set(AnimFlags::eBit15_bSemiTrans); diff --git a/Source/AliveLibAO/Explosion.cpp b/Source/AliveLibAO/Explosion.cpp index 93fd7072e..b1bee747b 100644 --- a/Source/AliveLibAO/Explosion.cpp +++ b/Source/AliveLibAO/Explosion.cpp @@ -23,7 +23,7 @@ Explosion* Explosion::ctor_458B80(FP xpos, FP ypos, FP exposion_size) ctor_417C10(); SetVTable(this, 0x4BC218); field_4_typeId = Types::eExplosion_74; - const AnimRecord rec = AO::AnimRec(AnimId::Explosion); + const AnimRecord rec = AO::AnimRec(AnimId::AirExplosion); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); Animation_Init_417FD0(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1); @@ -155,7 +155,7 @@ void Explosion::VUpdate_458D00() if (field_10_anim.field_92_current_frame == 1) { - const AnimRecord& rec = AO::AnimRec(AnimId::Explosion); + const AnimRecord& rec = AO::AnimRec(AnimId::AirExplosion); const auto ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); if (ppRes) { diff --git a/Source/AliveLibAO/Map.cpp b/Source/AliveLibAO/Map.cpp index d47a76a84..98e1fe002 100644 --- a/Source/AliveLibAO/Map.cpp +++ b/Source/AliveLibAO/Map.cpp @@ -1606,6 +1606,7 @@ Camera* Map::Create_Camera_445BE0(s16 xpos, s16 ypos, s32 /*a4*/) void Map::Create_FG1s_4447D0() { + /* pScreenManager_4FF7C8->UnsetDirtyBits_FG1_406EF0(); Camera* pCamera = field_34_camera_array[0]; @@ -1619,6 +1620,7 @@ void Map::Create_FG1s_4447D0() if (*ppRes) { + ResourceManager::Header* pHeader = ResourceManager::Get_Header_455620(ppRes); if (pHeader->field_8_type == ResourceManager::Resource_FG1) { @@ -1627,6 +1629,7 @@ void Map::Create_FG1s_4447D0() } } } + */ } // OG only allows for 30 paths, given the editor allows for the full 99 we have to use a bigger array in a non ABI breaking way diff --git a/Source/AliveLibAO/Movie.cpp b/Source/AliveLibAO/Movie.cpp index 732ce6f20..fd4a77b2e 100644 --- a/Source/AliveLibAO/Movie.cpp +++ b/Source/AliveLibAO/Movie.cpp @@ -239,8 +239,9 @@ EXPORT void CC Get_fmvs_sectors_44FEB0(const char_type* pMovieName1, const char_ } } -Movie* Movie::ctor_489C90(s32 id, s32 /*pos*/, s8 bUnknown, s32 /*flags*/, s16 volume) +Movie* Movie::ctor_489C90(s32 /* id*/, s32 /*pos*/, s8 /* bUnknown*/, s32 /*flags*/, s16 /* volume*/) { + /* AE_IMPLEMENTED(); @@ -251,7 +252,7 @@ Movie* Movie::ctor_489C90(s32 id, s32 /*pos*/, s8 bUnknown, s32 /*flags*/, s16 v field_6_flags.Set(Options::eSurviveDeathReset_Bit9); field_6_flags.Set(Options::eUpdateDuringCamSwap_Bit10); - /* + // TODO: FIX MOI PSX_Pos_To_CdLoc_49B340(pos, &loc); @@ -263,7 +264,7 @@ Movie* Movie::ctor_489C90(s32 id, s32 /*pos*/, s8 bUnknown, s32 /*flags*/, s16 v field_35 = BYTE1(sMovie_ref_count_9F309C); field_36 = sMovie_ref_count_9F309C; field_34_min = sMovie_ref_count_9F309C; - */ + field_28 = id; field_4_typeId = Types::eMovie_100; @@ -276,7 +277,9 @@ Movie* Movie::ctor_489C90(s32 id, s32 /*pos*/, s8 bUnknown, s32 /*flags*/, s16 v ResourceManager::Reclaim_Memory_455660(0); IO_Init_494230(); // Set up IO funcs + */ + field_6_flags.Set(BaseGameObject::Options::eDead_Bit3); return this; } diff --git a/Source/AliveLibAO/Mudokon.cpp b/Source/AliveLibAO/Mudokon.cpp index 7538fcf33..081d7b6f8 100644 --- a/Source/AliveLibAO/Mudokon.cpp +++ b/Source/AliveLibAO/Mudokon.cpp @@ -545,8 +545,8 @@ const AnimId sMudFrameTables_4CD330[64] = { AnimId::Mudokon_WalkToIdle, AnimId::Mudokon_MidWalkToIdle, AnimId::Mudokon_Idle, - AnimId::Mudokon_AO_Null, - AnimId::Mudokon_AO_LiftUse, + AnimId::Mudokon_LiftUseUp, + AnimId::Mudokon_LiftUseDown, AnimId::Mudokon_LiftGrabBegin, AnimId::Mudokon_LiftGrabEnd, AnimId::Mudokon_LeverUse, @@ -580,12 +580,12 @@ const AnimId sMudFrameTables_4CD330[64] = { AnimId::Mudokon_RunJumpBegin, AnimId::Mudokon_RunJumpMid, AnimId::Mudokon_StandToRun, - AnimId::Mudokon_FallLandDie, + AnimId::Mudokon_KnockForward, AnimId::Mudokon_Knockback, AnimId::Mudokon_KnockbackGetUp, AnimId::Mudokon_WalkOffEdge, - AnimId::Mudokon_LandSoft, - AnimId::Mudokon_Fall, + AnimId::Mudokon_AO_LandSoft_Long, + AnimId::Mudokon_FallingFromGrab, // check/rename AnimId::Mudokon_Chant, AnimId::Mudokon_ChantEnd, AnimId::Mudokon_ToDuck, @@ -597,7 +597,7 @@ const AnimId sMudFrameTables_4CD330[64] = { AnimId::Mudokon_CrouchChantToStruggle, AnimId::Mudokon_DuckKnockback, AnimId::Mudokon_PoisonGasDeath, - AnimId::None}; + AnimId::None }; void Mudokon::VUpdateResBlock_43EDB0() { diff --git a/Source/AliveLibAO/ScreenManager.cpp b/Source/AliveLibAO/ScreenManager.cpp index 35d6c7bf2..d03a15566 100644 --- a/Source/AliveLibAO/ScreenManager.cpp +++ b/Source/AliveLibAO/ScreenManager.cpp @@ -5,6 +5,8 @@ #include "VRam.hpp" #include "stdlib.hpp" #include "../AliveLibAE/Renderer/IRenderer.hpp" +#include "Map.hpp" +#include "PathData.hpp" #undef min #undef max @@ -93,6 +95,15 @@ void ScreenManager::DecompressCameraToVRam_407110(u16** ppBits) field_58_20x16_dirty_bits[2] = {}; field_58_20x16_dirty_bits[3] = {}; } + + char camName[32] = {}; + Path_Format_CameraName_4346B0( + camName, + gMap_507BA8.field_0_current_level, + gMap_507BA8.field_2_current_path, + gMap_507BA8.field_4_current_camera); + + IRenderer::GetRenderer()->LoadExternalCam(camName, reinterpret_cast(*ppBits), 512); } void ScreenManager::InvalidateRect_406CC0(s32 x, s32 y, s32 width, s32 height) @@ -243,15 +254,6 @@ void ScreenManager::VRender_406A60(PrimHeader** ppOt) return; } -#if RENDERER_OPENGL - // TODO: A custom sprite prim with magic numbers - // to trigger proper order rendering of our cam. - static Prim_Sprt MagicBackgroundPrim; - Sprt_Init(&MagicBackgroundPrim); - SetRGB0(&MagicBackgroundPrim, 255, 254, 253); - OrderingTable_Add_498A80(OtLayer(ppOt, Layer::eLayer_1), &MagicBackgroundPrim.mBase.header); -#endif - PSX_DrawSync_496750(0); for (s32 i = 0; i < 300; i++) diff --git a/Source/AliveLibAO/SecurityDoor.cpp b/Source/AliveLibAO/SecurityDoor.cpp index 95b435303..cc4ca8736 100644 --- a/Source/AliveLibAO/SecurityDoor.cpp +++ b/Source/AliveLibAO/SecurityDoor.cpp @@ -62,7 +62,7 @@ SecurityDoor* SecurityDoor::ctor_461840(Path_SecurityDoor* pTlv, s32 tlvInfo) SetVTable(this, 0x4BC918); - const AnimRecord rec = AO::AnimRec(AnimId::Security_Door); + const AnimRecord rec = AO::AnimRec(AnimId::Security_Door_Idle); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); Animation_Init_417FD0(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1); diff --git a/Source/AliveLibAO/Spark.cpp b/Source/AliveLibAO/Spark.cpp index aee9ae0d6..2e774a4e6 100644 --- a/Source/AliveLibAO/Spark.cpp +++ b/Source/AliveLibAO/Spark.cpp @@ -72,7 +72,7 @@ Spark* Spark::ctor_477B70(FP xpos, FP ypos, FP scale, u8 count, s16 min, s16 max field_50_timer = gnFrameCount_507670 + 3; - const AnimRecord& rec = AO::AnimRec(AnimId::Zap_Sparks); + const AnimRecord& rec = AO::AnimRec(AnimId::ChantOrb_Particle_Small); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); auto pParticle = ao_new(); if (pParticle) diff --git a/Source/AliveLibAO/Sparks.cpp b/Source/AliveLibAO/Sparks.cpp index f8e80d157..1bc8f02f4 100644 --- a/Source/AliveLibAO/Sparks.cpp +++ b/Source/AliveLibAO/Sparks.cpp @@ -14,7 +14,7 @@ Sparks* Sparks::ctor_40A3A0(FP xpos, FP ypos, FP scale) SetVTable(this, 0x4BA358); field_4_typeId = Types::eSpark_15; - const AnimRecord rec = AO::AnimRec(AnimId::Zap_Sparks); + const AnimRecord rec = AO::AnimRec(AnimId::ChantOrb_Particle_Small); u8** ppRes = ResourceManager::GetLoadedResource_4554F0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 0); Animation_Init_417FD0(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1); diff --git a/Source/AliveLibCommon/AnimResources.cpp b/Source/AliveLibCommon/AnimResources.cpp index 17a1eb1ad..da274813f 100644 --- a/Source/AliveLibCommon/AnimResources.cpp +++ b/Source/AliveLibCommon/AnimResources.cpp @@ -136,7 +136,7 @@ constexpr CombinedBgAnimRecord kBgAnimRecords[] = { constexpr AnimDetails kNullAnimDetails = {}; -constexpr CombinedAnimRecord kAnimRecords[] = { +constexpr CombinedAnimRecord kAnimRecords[916] = { {AnimId::None, kNullAnimDetails, kNullAnimDetails}, {AnimId::Abe_Head_Gib, { "ABEBLOW.BAN", 7732, 50, 25, kAbeblowResID, PalId::Default}, @@ -164,7 +164,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Phleg_Body_Gib, { "PHLEG.BND", 8188, 48, 28, kPhlegResID_807, PalId::Default}, kNullAnimDetails }, {AnimId::Fleech_Head_Gib, { "FLEEBLOW.BAN", 1088, 19, 8, kFleeBlowResID_580, PalId::Default}, kNullAnimDetails }, {AnimId::Fleech_Body_Gib, { "FLEEBLOW.BAN", 1128, 19, 8, kFleeBlowResID_580, PalId::Default}, kNullAnimDetails }, - + {AnimId::Elum_Head_Gib, kNullAnimDetails, {"ELMBLOW.BAN", 7440, 72, 43, AO::kElmblowAOResID_217, PalId::Default}}, {AnimId::Elum_Arm_Gib, kNullAnimDetails, {"ELMBLOW.BAN", 7480, 72, 43, AO::kElmblowAOResID_217, PalId::Default}}, {AnimId::Elum_Body_Gib, kNullAnimDetails, {"ELMBLOW.BAN", 7400, 72, 43, AO::kElmblowAOResID_217, PalId::Default}}, @@ -193,9 +193,10 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"DOGBLOW.BAN", 28416, 111, 63, AO::kSlogBlowAOResID, PalId::Default}}, {AnimId::Stick_Gib, kNullAnimDetails, { "STICK.BAN", 2800, 47, 29, AO::kStickGibAOResID, PalId::Default} }, - + {AnimId::Bat_Flying, kNullAnimDetails, { "BATBASIC.BAN", 6644, 48, 17, AO::kBatBasicAOResID, PalId::Default} }, {AnimId::Bat, kNullAnimDetails, { "BATBASIC.BAN", 6560, 48, 17, AO::kBatBasicAOResID, PalId::Default} }, + {AnimId::Bat_Unknown, kNullAnimDetails, { "BATBASIC.BAN", 6608, 48, 17, AO::kBatBasicAOResID, PalId::Default} }, {AnimId::Bee_Swarm, kNullAnimDetails, { "WASP.BAN", 636, 7, 4, AO::kWaspAOResID, PalId::Default} }, // Note: Fleech animations are sorted by order of appearance under Fleech.cpp (line 235) @@ -219,23 +220,23 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Fleech_SleepingWithTongue, { "FLEECH.BAN", 38396, 73, 35u, kFleechResID, PalId::Default}, kNullAnimDetails }, {AnimId::Fleech_Consume, { "FLEECH.BAN", 38276, 73, 35u, kFleechResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Idle, { "FLYSLIG.BND", 116888, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Horizontal, { "FLYSLIG.BND", 116912, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Idle_Turn_Around, { "FLYSLIG.BND", 117084, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Down, { "FLYSLIG.BND", 116988, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Down_Turn_Around, { "FLYSLIG.BND", 117584, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Up, { "FLYSLIG.BND", 117012, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Up_Turn_Around, { "FLYSLIG.BND", 117616, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Pull_Lever, { "FLYSLIG.BND", 117188, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Speak, { "FLYSLIG.BND", 117132, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Possession, { "FLYSLIG.BND", 117524, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Horizontal_End, { "FLYSLIG.BND", 117060, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Up_Start, { "FLYSLIG.BND", 117316, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Horizontal_To_Down, { "FLYSLIG.BND", 117276, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Up_To_Horizontal, { "FLYSLIG.BND", 117444, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Move_Down_To_Horizontal, { "FLYSLIG.BND", 117376, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Turn_Quick, { "FLYSLIG.BND", 116936, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Flying_Slig_Idle_To_Horizontal, { "FLYSLIG.BND", 117036, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_Idle, { "FLYSLIG.BND", 116888, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveHorizontal, { "FLYSLIG.BND", 116912, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_IdleTurnAround, { "FLYSLIG.BND", 117084, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveDown, { "FLYSLIG.BND", 116988, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveDownTurnAround, { "FLYSLIG.BND", 117584, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveUp, { "FLYSLIG.BND", 117012, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveUpTurnAround, { "FLYSLIG.BND", 117616, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_PullLever, { "FLYSLIG.BND", 117188, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_Speak, { "FLYSLIG.BND", 117132, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_Possession, { "FLYSLIG.BND", 117524, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveHorizontalEnd, { "FLYSLIG.BND", 117060, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveUpStart, { "FLYSLIG.BND", 117316, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveHorizontalToDown, { "FLYSLIG.BND", 117276, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveUpToHorizontal, { "FLYSLIG.BND", 117444, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_MoveDownToHorizontal, { "FLYSLIG.BND", 117376, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_TurnQuick, { "FLYSLIG.BND", 116936, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, + {AnimId::FlyingSlig_IdleToHorizontal, { "FLYSLIG.BND", 117036, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, {AnimId::FlyingSlig_BeginDownMovement, { "FLYSLIG.BND", 117336, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, {AnimId::FlyingSlig_EndDownMovement, { "FLYSLIG.BND", 117356, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, {AnimId::FlyingSlig_DownKnockback, { "FLYSLIG.BND", 117396, 107, 48u, kFlySligResID, PalId::Default}, kNullAnimDetails }, @@ -367,10 +368,9 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Mine_Car_Tread_Move_B, { "BAYROLL.BAN", 20788, 130, 62u, kBayrollResID_6013, PalId::Default}, kNullAnimDetails }, // The Mudokon section includes all of abe's animations as well since they share many animations - // TODO: correct BAN/BND's and resourceID's for AO {AnimId::Mudokon_ToSpeak, { "ABEBSIC1.BAN", 59112, 135, 80, kAbebsic1ResID, PalId::Default}, - {"ABEBSIC1.BAN", 256136, 135, 80, AO::kAbebsic1AOResID, PalId::Default}}, + {"ABEBSIC.BAN", 256136, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, {AnimId::Mudokon_Null, { "ABEBSIC.BAN", 270240, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails }, {AnimId::Mudokon_CrouchSpeak, { "ABEBSIC.BAN", 271080, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails }, {AnimId::Mudokon_ToCrouchSpeak, { "ABEBSIC.BAN", 271120, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails }, @@ -441,11 +441,11 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "ABEWELL.BAN", 20404, 135, 80, kAbewellResID, PalId::Default}, {"ABEWELL.BAN", 19476, 135, 80, AO::kAbewellAOResID, PalId::Default}}, {AnimId::Mudokon_FallLandDie, - { "ABESMASH.BAN", 8104, 135, 80, kAbesmashResID, PalId::Default}, - {"ABEKNFD.BAN", 16772, 135, 80, AO::kAbeknfdAOResID, PalId::Default}}, + { "ABENOELM.BND", 8104, 135, 80, kAbesmashResID, PalId::Default}, + {"ABENOELM.BND", 7880, 135, 80, AO::kAbesmashAOResID, PalId::Default}}, {AnimId::Mudokon_Fall, - { "ABEFALL.BAN", 5724, 135, 80, kAbefallResID, PalId::Default}, - {"ABEEDGE.BAN", 40652, 135, 80, AO::kAbeedgeAOResID, PalId::Default}}, + { "ABENOELM.BND", 5724, 135, 80, kAbefallResID, PalId::Default}, + {"ABENOELM.BND", 5560, 135, 80, AO::kAbefallAOResID, PalId::Default}}, {AnimId::Mudokon_HandstoneBegin, { "ABESTONE.BAN", 16096, 135, 80, kAbestoneResID, PalId::Default}, {"ABESTONE.BAN", 15484, 135, 80, AO::kAbestoneAOResID, PalId::Default}}, @@ -468,7 +468,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "ABEEDGE.BAN", 32152, 135, 80, kAbeedgeResID, PalId::Default}, {"ABEEDGE.BAN", 40836, 135, 80, AO::kAbeedgeAOResID, PalId::Default}}, {AnimId::Mudokon_RollOffEdge, // rename to roll off ledge? - { "ABEEDGE.BAN", 32132, 135, 80, kAbeedgeResID, PalId::Default}, + { "ABEEDGE.BAN", 32132, 135, 80, kAbeedgeResID, PalId::Default}, {"ABEEDGE.BAN", 40816, 135, 80, AO::kAbeedgeAOResID, PalId::Default}}, {AnimId::Mudokon_SlapBomb, { "ABEBOMB.BAN", 6520, 135, 80, kAbebombResID, PalId::Default}, @@ -506,13 +506,14 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Mudokon_DoorExit, { "ABEDOOR.BAN", 19088, 135, 80, kAbedoorResID, PalId::Default}, {"ABEDOOR.BAN", 17568, 135, 80, AO::kAbedoorAOResID, PalId::Default}}, - {AnimId::Mudokon_ToShrykull, + {AnimId::Mudokon_ToShrykull, { "ABEMORPH.BAN", 8732, 135, 80, kAbemorphResID, PalId::Default}, - { "ABEMORPH.BAN", 92004, 121, 79, AO::kShrmorphAOResID, PalId::Default} + { "ABEMORPH.BAN", 8240, 135, 80, AO::kAbemorphAOResID, PalId::Default} }, {AnimId::Mudokon_EndShrykull, { "ABEMORPH.BAN", 8772, 135, 80, kAbemorphResID, PalId::Default}, - kNullAnimDetails }, + { "ABEMORPH.BAN", 8280, 135, 80, AO::kAbemorphAOResID, PalId::Default} + }, {AnimId::Mudokon_LiftGrabBegin, { "ABELIFT.BAN", 22548, 135, 80, kAbeliftResID, PalId::Default}, @@ -523,16 +524,20 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Mudokon_LiftGrabIdle, { "ABELIFT.BAN", 22464, 135, 80, kAbeliftResID, PalId::Default}, {"ABELIFT.BAN", 21612, 135, 80, AO::kAbeliftAOResID, PalId::Default}}, - {AnimId::Mudokon_LiftUseUp, { "ABELIFT.BAN", 22596, 135, 80, kAbeliftResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Mudokon_LiftUseDown, { "ABELIFT.BAN", 22496, 135, 80, AO::kAbeliftAOResID, PalId::Default}, kNullAnimDetails }, - - {AnimId::Mudokon_AO_LiftUse, kNullAnimDetails, {"ABELIFT.BAN", 21644, 135, 80, AO::kAbeliftAOResID, PalId::Default}}, + {AnimId::Mudokon_LiftUseUp, + { "ABELIFT.BAN", 22596, 135, 80, kAbeliftResID, PalId::Default}, + {"ABELIFT.BAN", 21744, 135, 80, AO::kAbeliftAOResID, PalId::Default} + }, + {AnimId::Mudokon_LiftUseDown, + { "ABELIFT.BAN", 22496, 135, 80, AO::kAbeliftAOResID, PalId::Default}, + {"ABELIFT.BAN", 21644, 135, 80, AO::kAbeliftAOResID, PalId::Default} + }, {AnimId::Mudokon_PoisonGasDeath, { "ABEGAS.BAN", 28824, 135, 80, kAbegasResID, PalId::Default}, {"ABEGAS.BAN", 27748, 135, 80, AO::kAbegasAOResID, PalId::Default}}, - {AnimId::Mudokon_Idle, - { "ABEBSIC1.BAN", 58888, 135, 80, kAbebsic1ResID, PalId::Default}, + {AnimId::Mudokon_Idle, + { "ABEBSIC1.BAN", 58888, 135, 80, kAbebsic1ResID, PalId::Default}, {"ABEBSIC1.BAN", 55968, 135, 80, AO::kAbebsic1AOResID, PalId::Default}}, {AnimId::Mudokon_Walk, { "ABEBSIC1.BAN", 58808, 135, 80, kAbebsic1ResID, PalId::Default}, @@ -562,14 +567,12 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "ABEBSIC1.BAN", 58748, 135, 80, kAbebsic1ResID, PalId::Default}, {"ABEBSIC1.BAN", 55828, 135, 80, AO::kAbebsic1AOResID, PalId::Default}}, - {AnimId::Mudokon_AO_Null, kNullAnimDetails, {"ABEBSIC1.BAN", 21744, 135, 80, AO::kAbebsic1AOResID, PalId::Default}}, - {AnimId::Mudokon_LeverUse, - { "ABEPULL.BAN", 11396, 135, 80, kAbepullResID, PalId::Default}, + { "ABEPULL.BAN", 11396, 135, 80, kAbepullResID, PalId::Default}, {"ABEPULL.BAN", 10988, 135, 80, AO::kAbepullAOResID, PalId::Default}}, {AnimId::Mudokon_CrouchScrub, { "MUDSCRUB.BAN", 9388, 135, 80, kMudscrubResID, PalId::Default}, - {"MUDWORK.BND", 9056, 135, 80, AO::kMudscrubAOResID, PalId::Default}}, + {"MUDSCRUB.BAN", 9056, 135, 80, AO::kMudscrubAOResID, PalId::Default}}, {AnimId::Mudokon_CrouchIdle, { "ABEBSIC.BAN", 270092, 135, 80, kAbebasicResID, PalId::Default}, {"ABEBSIC.BAN", 255600, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, @@ -584,7 +587,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"ABEBSIC.BAN", 255568, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, {AnimId::Mudokon_WalkToRun, { "ABEBSIC.BAN", 270120, 135, 80, kAbebasicResID, PalId::Default}, - {"MUDLOTUS.BND", 255628, 96, 55, AO::kAbebasicAOResID, PalId::Default}}, + {"ABEBSIC.BAN", 255628, 96, 55, AO::kAbebasicAOResID, PalId::Default}}, {AnimId::Mudokon_MidWalkToRun, { "ABEBSIC.BAN", 270268, 135, 80, kAbebasicResID, PalId::Default}, {"ABEBSIC.BAN", 255808, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, @@ -641,17 +644,25 @@ constexpr CombinedAnimRecord kAnimRecords[] = { // 11396 for AO means no frametableoffset specified yet {AnimId::Mudokon_StandToRun, { "ABEBSIC.BAN", 270252, 135, 80, kAbebasicResID, PalId::Default}, - {"MUDLOTUS.BND", 255788, 96, 55, AO::kAbebasicAOResID, PalId::Default}}, + {"ABEBSIC.BAN", 255788, 96, 55, AO::kAbebasicAOResID, PalId::Default}}, {AnimId::Mudokon_HoistBegin, { "ABEBSIC.BAN", 269976, 135, 80, kAbebasicResID, PalId::Default}, - {"ABESIC.BAN", 255484, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, + {"ABEBSIC.BAN", 255484, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, + + // this has been removed from AE. it is almost identical to Mudokon_HoistBegin + {AnimId::Mudokon_AO_HoistBegin_Long, kNullAnimDetails, {"ABEEDGE.BAN", 40772, 135, 80, AO::kAbeedgeAOResID, PalId::Default} }, + {AnimId::Mudokon_HoistIdle, { "ABEBSIC.BAN", 270024, 135, 80, kAbebasicResID, PalId::Default}, {"ABEBSIC.BAN", 255532, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, {AnimId::Mudokon_LandSoft, { "ABEBSIC.BAN", 269928, 135, 80, kAbebasicResID, PalId::Default}, - {"ABEEDGE.BAN", 40868, 135, 80, AO::kAbeedgeAOResID, PalId::Default}}, + {"ABEBSIC.BAN", 255436, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, + + // this has been removed from AE. + {AnimId::Mudokon_AO_LandSoft_Long, kNullAnimDetails, {"ABEEDGE.BAN", 40868, 135, 80, AO::kAbeedgeAOResID, PalId::Default}}, + {AnimId::Mudokon_DunnoBegin, { "ABEBSIC.BAN", 270180, 135, 80, kAbebasicResID, PalId::Default}, {"ABEBSIC.BAN", 255716, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, @@ -668,8 +679,8 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "ABEEDGE.BAN", 32040, 135, 80, kAbeedgeResID, PalId::Default}, {"ABEEDGE.BAN", 40680, 96, 55, AO::kAbeedgeAOResID, PalId::Default}}, {AnimId::Mudokon_FallingFromGrab, - { "ABEEDGE.BAN", 32012, 135, 80, kAbeedgeResID, PalId::Default}, // TODO: swapped with walk of edge? - {"ABEKNFD.BAN", 17240, 135, 80, AO::kAbeknfdAOResID, PalId::Default}}, + { "ABEEDGE.BAN", 32012, 135, 80, kAbeedgeResID, PalId::Default}, + {"ABEEDGE.BAN", 40652, 135, 80, AO::kAbeedgeAOResID, PalId::Default} }, {AnimId::Mudokon_Chant, { "ABEOMM.BAN", 9992, 135, 80, kAbeommResID, PalId::Default}, {"ABEOMM.BAN", 9516, 135, 80, AO::kAbeommAOResID, PalId::Default}}, @@ -677,7 +688,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "ABEOMM.BAN", 10040, 135, 80, kAbeommResID, PalId::Default}, {"ABEOMM.BAN", 9564, 135, 80, AO::kAbeommAOResID, PalId::Default}}, {AnimId::Mudokon_ToDuck, - { "MUDWORK.BND", 5236, 135, 80, kMudoduckResID, PalId::Default}, + { "MUDWORK.BND", 5236, 135, 80, kMudoduckResID, PalId::Default}, {"MUDWORK.BND", 5096, 135, 80, AO::kMudoduckAOResID, PalId::Default}}, {AnimId::Mudokon_Duck, { "MUDWORK.BND", 5256, 135, 80, kMudoduckResID, PalId::Default}, @@ -687,7 +698,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"MUDWORK.BND", 5140, 135, 80, AO::kMudoduckAOResID, PalId::Default}}, {AnimId::Mudokon_DuckKnockback, { "MUDBTLNK.BAN", 5328, 135, 80, kMudbtlnkResID, PalId::Default}, - {"MUDWORK.BND", 5224, 135, 80, AO::kMudbtlnkAOResID, PalId::Default}}, + {"MUDBTLNK.BAN", 5224, 135, 80, AO::kMudbtlnkAOResID, PalId::Default}}, @@ -711,90 +722,77 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Mudokon_CrouchChant, kNullAnimDetails, {"MUDLOTUS.BND", 11052, 96, 55, AO::kMudltusAOResID, PalId::Default}}, {AnimId::Mudokon_CrouchChantToStruggle, kNullAnimDetails, {"MUDLOTUS.BND", 10996, 96, 55, AO::kMudltusAOResID, PalId::Default}}, - // DunnoMid only exists in AO for some reason - {AnimId::Mudokon_DunnoMid, kNullAnimDetails, {"MUDLOTUS.BND", 255688, 135, 80, kMudltusResID, PalId::Default}}, // todo ban/bnd - - {AnimId::Mudokon_AO_M_15_Null, kNullAnimDetails, {"MUDLOTUS.BND", 255776, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd - - {AnimId::Mudokon_CrouchSpeak1, kNullAnimDetails, {"MUDLOTUS.BND", 256660, 135, 80, kMudltusResID, PalId::Default}}, // todo ban/bnd - {AnimId::Mudokon_CrouchSpeak2, kNullAnimDetails, {"MUDLOTUS.BND", 256700, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd - - {AnimId::Mudokon_ElumWalkLoop, kNullAnimDetails, {"ABEWELM.BND", 144920, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumIdle, kNullAnimDetails, {"ABEWELM.BND", 145132, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunSlideStop, kNullAnimDetails, {"ABEWELM.BND", 145736, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunTurn, kNullAnimDetails, {"ABEWELM.BND", 145804, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_AO_M_106_Null, kNullAnimDetails, {"ABEWELM.BND", 145000, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumHopBegin, kNullAnimDetails, {"ABEWELM.BND", 145104, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumHopMid, kNullAnimDetails, {"ABEWELM.BND", 145164, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumHopLand, kNullAnimDetails, {"ABEWELM.BND", 145196, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunJumpBegin, kNullAnimDetails, {"ABEWELM.BND", 145220, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunJumpMid, kNullAnimDetails, {"ABEWELM.BND", 145688, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunJumpLand, kNullAnimDetails, {"ABEWELM.BND", 145240, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumTurn, kNullAnimDetails, {"ABEWELM.BND", 145456, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunLoop, kNullAnimDetails, {"ABEWELM.BND", 145312, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumSpeak, kNullAnimDetails, {"ABEWELM.BND", 145512, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_AO_M_116_Null, kNullAnimDetails, {"ABEWELM.BND", 145588, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumWalkBegin, kNullAnimDetails, {"ABEWELM.BND", 145608, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunBegin, kNullAnimDetails, {"ABEWELM.BND", 145548, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_AO_M_119_Null, kNullAnimDetails, {"ABEWELM.BND", 145416, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunToWalk, kNullAnimDetails, {"ABEWELM.BND", 145436, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumMidRunToWalk, kNullAnimDetails, {"ABEWELM.BND", 145568, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunTurnToRun, kNullAnimDetails, {"ABEWELM.BND", 145272, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumRunTurnToWalk, kNullAnimDetails, {"ABEWELM.BND", 145292, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumWalkEnd, kNullAnimDetails, {"ABEWELM.BND", 145648, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumMidWalkEnd, kNullAnimDetails, {"ABEWELM.BND", 145668, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - {AnimId::Mudokon_ElumBeesStruggling, kNullAnimDetails, {"ABEWELM.BND", 145860, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, - - {AnimId::Mudokon_ElumMountEnd, kNullAnimDetails, {"ABEWELM.BND", 23568, 135, 80, AO::kElumUnknownAOResID_110, PalId::Default}}, // todo ban/bnd - {AnimId::Mudokon_ElumUnmountBegin, kNullAnimDetails, {"ANEMNT.BAN", 13716, 135, 80, AO::kElumUnknownAOResID_112, PalId::Default}}, // todo ban/bnd - - {AnimId::Mudokon_ElumUnmountEnd, kNullAnimDetails, {"ANEDSMNT.BND", 34676, 135, 80, AO::kAbeANEDSMNTAOResID_113, PalId::Default} }, - {AnimId::Mudokon_ElumMountBegin, kNullAnimDetails, {"ANEPRMNT.BAN", 16256, 135, 80, AO::kAneprmntAOResID, PalId::Default}}, - - {AnimId::Mudokon_ElumFallOffEdge, kNullAnimDetails, {"ANEEDGE.BAN", 21672, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, - {AnimId::Mudokon_ElumFall, kNullAnimDetails, {"ANEEDGE.BAN", 21700, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, - {AnimId::Mudokon_ElumLand, kNullAnimDetails, {"ANEEDGE.BAN", 21724, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, - {AnimId::Mudokon_ElumJumpToFall, kNullAnimDetails, {"ANEEDGE.BAN", 21780, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, - - {AnimId::Mudokon_ElumKnockback, kNullAnimDetails, {"ANEKNBK.BAN", 11652, 135, 80, AO::kAneknbkAOResID, PalId::Default}}, - - {AnimId::Mudokon_Idle_RubEyes, kNullAnimDetails, {"ABERUB.BAN", 9860, 135, 80, AO::kAberubAOResID, PalId::Default}}, - {AnimId::Mudokon_Idle_StretchArms, kNullAnimDetails, {"ABESIZE.BAN", 13152, 135, 80, AO::kAbesizeAOResID, PalId::Default}}, - {AnimId::Mudokon_Idle_Yawn, kNullAnimDetails, {"ABEYAWN.BAN", 12392, 135, 80, AO::kAbeyawnAOResID, PalId::Default}}, - - {AnimId::Mudokon_AO_ToShrykull, kNullAnimDetails, {"MUDLOTUS.BND", 8240, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd + resource id - {AnimId::Mudokon_AO_ShrykullEnd, kNullAnimDetails, {"MUDLOTUS.BND", 8280, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd + resource id - - - {AnimId::Mudokon_AO_Unknown1, kNullAnimDetails, {"MUDLOTUS.BND", 40772, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd + resource id - {AnimId::Mudokon_AO_Unknown2, kNullAnimDetails, {"MUDLOTUS.BND", 5560, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd + resource id - {AnimId::Mudokon_AO_Unknown3, kNullAnimDetails, {"MUDLOTUS.BND", 7880, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd + resource id - {AnimId::Mudokon_AO_Unknown4, kNullAnimDetails, {"MUDLOTUS.BND", 255436, 135, 80, AO::kMudltusAOResID, PalId::Default}}, // todo ban/bnd + resource id - - - {AnimId::Mudokon_KnockForward, - {"ABEKNFD.BAN", 17240, 135, 80, kAbeknfdResID, PalId::Default}, - {"ABEKNFD.BAN", 17240, 135, 80, AO::kAbeknfdAOResID, PalId::Default}}, - - - {AnimId::Mudokon_MineCarEnter, {"ABECAR.BAN", 8540, 135, 80, kAbeCarResId, PalId::Default}, kNullAnimDetails}, - {AnimId::Mudokon_MineCarExit, {"ABECAR.BAN", 8588, 135, 80, kAbeCarResId, PalId::Default}, kNullAnimDetails}, - {AnimId::Mudokon_Sorry, {"ABEBSIC.BAN", 271192, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails}, - {AnimId::Mudokon_AfterSorry, {"ABEBSIC.BAN", 271248, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails}, - - {AnimId::Mudokon_Chisel, {"MUDCHSL.BAN", 5276, 135, 80, kMudchslResID, PalId::Default}, kNullAnimDetails}, - {AnimId::Mudokon_StartChisel, {"MUDCHSL.BAN", 5308, 135, 80, kMudchslResID, PalId::Default}, kNullAnimDetails}, - {AnimId::Mudokon_StopChisel, {"MUDCHSL.BAN", 5320, 135, 80, kMudchslResID, PalId::Default}, kNullAnimDetails}, - {AnimId::Mudokon_Punch,{"ABEBSIC.BAN", 271152, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails}, - {AnimId::Mudokon_SlapOwnHead, { "MUDIDLE.BAN", 9640, 135, 80, kMudidleResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Mudokon_TurnWheelBegin, { "ABEWORK.BAN", 11856, 135, 80, kAbeworkResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Mudokon_TurnWheel, { "ABEWORK.BAN", 11816, 135, 80, kAbeworkResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Mudokon_TurnWheelEnd, { "ABEWORK.BAN", 11888, 135, 80, kAbeworkResID, PalId::Default}, kNullAnimDetails }, + // DunnoMid only exists in AO for some reason + {AnimId::Mudokon_DunnoMid, kNullAnimDetails, {"ABEBSIC.BAN", 255688, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, + + {AnimId::Mudokon_AO_M_15_Null, kNullAnimDetails, {"ABEBSIC.BAN", 255776, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, + + {AnimId::Mudokon_CrouchSpeak1, kNullAnimDetails, {"ABEBSIC.BAN", 256660, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, + {AnimId::Mudokon_CrouchSpeak2, kNullAnimDetails, {"ABEBSIC.BAN", 256700, 135, 80, AO::kAbebasicAOResID, PalId::Default}}, + + {AnimId::Mudokon_ElumWalkLoop, kNullAnimDetails, {"ABEWELM.BND", 144920, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumIdle, kNullAnimDetails, {"ABEWELM.BND", 145132, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunSlideStop, kNullAnimDetails, {"ABEWELM.BND", 145736, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunTurn, kNullAnimDetails, {"ABEWELM.BND", 145804, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_AO_M_106_Null, kNullAnimDetails, {"ABEWELM.BND", 145000, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumHopBegin, kNullAnimDetails, {"ABEWELM.BND", 145104, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumHopMid, kNullAnimDetails, {"ABEWELM.BND", 145164, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumHopLand, kNullAnimDetails, {"ABEWELM.BND", 145196, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunJumpBegin, kNullAnimDetails, {"ABEWELM.BND", 145220, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunJumpMid, kNullAnimDetails, {"ABEWELM.BND", 145688, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunJumpLand, kNullAnimDetails, {"ABEWELM.BND", 145240, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumTurn, kNullAnimDetails, {"ABEWELM.BND", 145456, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunLoop, kNullAnimDetails, {"ABEWELM.BND", 145312, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumSpeak, kNullAnimDetails, {"ABEWELM.BND", 145512, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_AO_M_116_Null, kNullAnimDetails, {"ABEWELM.BND", 145588, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumWalkBegin, kNullAnimDetails, {"ABEWELM.BND", 145608, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunBegin, kNullAnimDetails, {"ABEWELM.BND", 145548, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_AO_M_119_Null, kNullAnimDetails, {"ABEWELM.BND", 145416, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunToWalk, kNullAnimDetails, {"ABEWELM.BND", 145436, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumMidRunToWalk, kNullAnimDetails, {"ABEWELM.BND", 145568, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunTurnToRun, kNullAnimDetails, {"ABEWELM.BND", 145272, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumRunTurnToWalk, kNullAnimDetails, {"ABEWELM.BND", 145292, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumWalkEnd, kNullAnimDetails, {"ABEWELM.BND", 145648, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumMidWalkEnd, kNullAnimDetails, {"ABEWELM.BND", 145668, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + {AnimId::Mudokon_ElumBeesStruggling, kNullAnimDetails, {"ABEWELM.BND", 145860, 135, 80, AO::kAbeWElmAOResID_100, PalId::Default}}, + + {AnimId::Mudokon_ElumMountEnd, kNullAnimDetails, {"ANEMOUNT.BND", 23568, 135, 80, AO::kElumUnknownAOResID_110, PalId::Default}}, + {AnimId::Mudokon_ElumUnmountBegin, kNullAnimDetails, {"ABEWELM.BND", 13716, 135, 80, AO::kElumUnknownAOResID_112, PalId::Default}}, + + {AnimId::Mudokon_ElumUnmountEnd, kNullAnimDetails, {"ANEDSMNT.BND", 34676, 135, 80, AO::kAbeANEDSMNTAOResID_113, PalId::Default} }, + {AnimId::Mudokon_ElumMountBegin, kNullAnimDetails, {"ANEPRMNT.BAN", 16256, 135, 80, AO::kAneprmntAOResID, PalId::Default}}, + + {AnimId::Mudokon_ElumFallOffEdge, kNullAnimDetails, {"ANEEDGE.BAN", 21672, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, + {AnimId::Mudokon_ElumFall, kNullAnimDetails, {"ANEEDGE.BAN", 21700, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, + {AnimId::Mudokon_ElumLand, kNullAnimDetails, {"ANEEDGE.BAN", 21724, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, + {AnimId::Mudokon_ElumJumpToFall, kNullAnimDetails, {"ANEEDGE.BAN", 21780, 135, 80, AO::kAneedgeAOResID, PalId::Default}}, + + {AnimId::Mudokon_ElumKnockback, kNullAnimDetails, {"ANEKNBK.BAN", 11652, 135, 80, AO::kAneknbkAOResID, PalId::Default}}, + + {AnimId::Mudokon_Idle_RubEyes, kNullAnimDetails, {"ABERUB.BAN", 9860, 135, 80, AO::kAberubAOResID, PalId::Default}}, + {AnimId::Mudokon_Idle_StretchArms, kNullAnimDetails, {"ABESIZE.BAN", 13152, 135, 80, AO::kAbesizeAOResID, PalId::Default}}, + {AnimId::Mudokon_Idle_Yawn, kNullAnimDetails, {"ABEYAWN.BAN", 12392, 135, 80, AO::kAbeyawnAOResID, PalId::Default}}, + + {AnimId::Mudokon_KnockForward, + {"ABEKNFD.BAN", 17240, 135, 80, kAbeknfdResID, PalId::Default}, + {"ABEKNFD.BAN", 16772, 135, 80, AO::kAbeknfdAOResID, PalId::Default}}, + + {AnimId::Mudokon_MineCarEnter, {"ABECAR.BAN", 8540, 135, 80, kAbeCarResId, PalId::Default}, kNullAnimDetails}, + {AnimId::Mudokon_MineCarExit, {"ABECAR.BAN", 8588, 135, 80, kAbeCarResId, PalId::Default}, kNullAnimDetails}, + {AnimId::Mudokon_Sorry, {"ABEBSIC.BAN", 271192, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails}, + {AnimId::Mudokon_AfterSorry, {"ABEBSIC.BAN", 271248, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails}, + + {AnimId::Mudokon_Chisel, {"MUDCHSL.BAN", 5276, 135, 80, kMudchslResID, PalId::Default}, kNullAnimDetails}, + {AnimId::Mudokon_StartChisel, {"MUDCHSL.BAN", 5308, 135, 80, kMudchslResID, PalId::Default}, kNullAnimDetails}, + {AnimId::Mudokon_StopChisel, {"MUDCHSL.BAN", 5320, 135, 80, kMudchslResID, PalId::Default}, kNullAnimDetails}, + {AnimId::Mudokon_Punch,{"ABEBSIC.BAN", 271152, 135, 80, kAbebasicResID, PalId::Default}, kNullAnimDetails}, + {AnimId::Mudokon_SlapOwnHead, { "MUDIDLE.BAN", 9640, 135, 80, kMudidleResID, PalId::Default}, kNullAnimDetails }, + {AnimId::Mudokon_TurnWheelBegin, { "ABEWORK.BAN", 11856, 135, 80, kAbeworkResID, PalId::Default}, kNullAnimDetails }, + {AnimId::Mudokon_TurnWheel, { "ABEWORK.BAN", 11816, 135, 80, kAbeworkResID, PalId::Default}, kNullAnimDetails }, + {AnimId::Mudokon_TurnWheelEnd, { "ABEWORK.BAN", 11888, 135, 80, kAbeworkResID, PalId::Default}, kNullAnimDetails }, // AE only end - - // TODO: check AO ResourceID's {AnimId::Paramite_Idle, { "PARAMITE.BND", 96696, 137, 65u, kArjbasicResID, PalId::Default}, {"PARAMITE.BND", 57152, 138, 49, AO::kArjbasicAOResID, PalId::Default}}, @@ -813,7 +811,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Paramite_Hop, { "PARAMITE.BND", 96728, 137, 65u, kArjbasicResID, PalId::Default}, {"PARAMITE.BND", 57176, 138, 49, AO::kArjbasicAOResID, PalId::Default}}, - {AnimId::Paramite_Unused, { "PARAMITE.BND", 97164, 137, 65u, kArjbasicResID, PalId::Default}, kNullAnimDetails }, // crashes the game when used + {AnimId::Paramite_Unused, { "PARAMITE.BND", 97164, 137, 65u, kArjjumpResID, PalId::Default}, kNullAnimDetails }, {AnimId::Paramite_WalkRunTransition, { "PARAMITE.BND", 97172, 137, 65u, kArjbasicResID, PalId::Default}, {"PARAMITE.BND", 57348, 138, 49, AO::kArjbasicAOResID, PalId::Default}}, @@ -872,18 +870,14 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"PARAMITE.BND", 14728, 138, 49, AO::kArjeatAOResID, PalId::Default}}, {AnimId::Paramite_Death, { "PARAMITE.BND", 8108, 137, 65u, kArjfalrkResID, PalId::Default}, - {"PARAMITE.BND", 9132, 138, 49, AO::kArjfalrkAOResID, PalId::Default}}, - {AnimId::Paramite_Squawk, - { "PARAMITE.BND", 9636, 137, 65u, kArjwaspResID, PalId::Default}, - kNullAnimDetails }, + {"PARAMITE.BND", 11076, 138, 49, AO::kArjfalrkAOResID, PalId::Default} }, {AnimId::Paramite_Attack, { "PARAMITE.BND", 10948, 137, 65u, kArjscrchResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Paramite_Struggle, kNullAnimDetails, {"PARAMITE.BND", 11076, 138, 49, AO::kArjscrchAOResID, PalId::Default}}, // same as Paramite_Squawk? - {AnimId::Paramite_AO_M_7_Unknown, kNullAnimDetails, {"PARAMITE.BND", 57340, 138, 49, AO::kArjscrchAOResID, PalId::Default}}, + {AnimId::Paramite_Struggle, { "PARAMITE.BND", 9636, 137, 65u, kArjwaspResID, PalId::Default}, {"PARAMITE.BND", 9132, 138, 49, AO::kArjwaspAOResID, PalId::Default} }, + {AnimId::Paramite_AO_M_7_Unknown, kNullAnimDetails, {"PARAMITE.BND", 57340, 138, 49, AO::kArjbasicAOResID, PalId::Default}}, {AnimId::Paramite_AO_M_22_Unknown, kNullAnimDetails, {"PARAMITE.BND", 10520, 138, 49, AO::kArjscrchAOResID, PalId::Default}}, - // TODO: check AO ResourceID's {AnimId::Scrab_Idle, { "SCRAB.BND", 224764, 156, 69, kArsbasicResID, PalId::Default}, {"SCRAB.BND", 168644, 168, 69, AO::kArsbasicAOResID, PalId::Default}}, @@ -924,7 +918,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Scrab_Knockback, { "SCRAB.BND", 225208, 156, 69, kArsbasicResID, PalId::Default}, kNullAnimDetails }, {AnimId::Scrab_GetEaten, { "SCRAB.BND", 225280, 156, 69, kArsbasicResID, PalId::Default}, - {"SCRAB.BND", 1324, 168, 69, AO::kArsbasicAOResID, PalId::Default}}, + {"SCRAB.BND", 1324, 168, 69, AO::kArsdeadAOResID, PalId::Default}}, {AnimId::Scrab_Stamp, { "SCRAB.BND", 24480, 156, 69, kArsdanceResID, PalId::Default}, {"SCRAB.BND", 24136, 168, 69, AO::kArsdanceAOResID, PalId::Default}}, @@ -961,9 +955,9 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "SCRAB.BND", 15600, 156, 69, kArscrshResID, PalId::Default}, {"SCRAB.BND", 12724, 168, 69, AO::kArscrshAOResID, PalId::Default}}, - {AnimId::Scrab_AO_ToFall, kNullAnimDetails, {"SCRAB.BND", 168992, 168, 69, AO::kArseatAOResID, PalId::Default}}, + {AnimId::Scrab_AO_ToFall, kNullAnimDetails, {"SCRAB.BND", 168992, 168, 69, AO::kArsbasicAOResID, PalId::Default}}, {AnimId::Scrab_AO_ToFeed, kNullAnimDetails, {"SCRAB.BND", 19544, 168, 69, AO::kArseatAOResID, PalId::Default}}, - {AnimId::Scrab_AO_M_19_Unused, kNullAnimDetails, {"SCRAB.BND", 11060, 168, 69, AO::kArseatAOResID, PalId::Default}}, + {AnimId::Scrab_AO_M_19_Unused, kNullAnimDetails, {"SCRAB.BND", 11060, 168, 69, AO::kArsgrwlAOResID, PalId::Default}}, { AnimId::Crawling_Slig_Idle, { "CRAWLSLG.BND", 53684, 109, 37, kCrawlingSligResID_449, PalId::Default}, kNullAnimDetails }, { AnimId::Crawling_Slig_UsingButton, { "CRAWLSLG.BND", 53852, 109, 37, kCrawlingSligResID_449, PalId::Default}, kNullAnimDetails }, @@ -1088,7 +1082,9 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Slig_Smash, { "SLGKNFD.BAN", 13016, 160, 68, kSlgknfdResID, PalId::Default}, {"SLGKNFD.BAN", 12752, 160, 68, AO::kSlgknfdAOResID, PalId::Default}}, - {AnimId::Slig_PullLever, { "SLGLEVER.BAN", 12612, 160, 68, kSlgleverResID, PalId::Default}, kNullAnimDetails }, // AO doesn't have this?? + {AnimId::Slig_PullLever, + { "SLGLEVER.BAN", 12612, 160, 68, kSlgleverResID, PalId::Default}, + { "SLGLEVER.BAN", 12356, 160, 68, AO::kSlgleverAOResID, PalId::Default}}, {AnimId::Slig_LiftGrip, { "SLGLIFT.BAN", 23048, 160, 68, kSlgliftResID, PalId::Default}, {"SLGLIFT.BAN", 22628, 160, 68, AO::kSlgliftAOResID, PalId::Default}}, @@ -1118,7 +1114,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::MenuAbeSpeak_WhistleLow, kNullAnimDetails, {"ABESPEAK.BAN", 2115824, 134, 90, AO::kAbespeakAOResID, PalId::Default}}, {AnimId::MenuAbeSpeak_Fart, kNullAnimDetails, {"ABESPEAK.BAN", 2115368, 134, 90, AO::kAbespeakAOResID, PalId::Default}}, - { AnimId::MenuAbeSpeak_Hello, + { AnimId::MenuAbeSpeak_Hello, {"ABESPEK2.BAN", 247620, 233, 125, kAbespek2ResID, PalId::Default}, {"STARTANM.BND", 201320, 134, 90, AO::kAbespek2AOResID, PalId::Default}}, @@ -1130,11 +1126,11 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"ABESPEK2.BAN", 247684, 233, 125, kAbespek2ResID, PalId::Default}, {"STARTANM.BND", 201384, 134, 90, AO::kAbespek2AOResID, PalId::Default}}, - { AnimId::MenuAbeSpeak_Ok, + { AnimId::MenuAbeSpeak_Ok, {"ABESPEK2.BAN", 247924, 233, 125, kAbespek2ResID, PalId::Default}, {"STARTANM.BND", 201632, 134, 90, AO::kAbespek2AOResID, PalId::Default}}, - { AnimId::MenuAbeSpeak_FollowMe, + { AnimId::MenuAbeSpeak_FollowMe, {"ABESPEAK.BAN", 693672, 233, 125, kAbespeakResID, PalId::Default}, {"ABESPEAK.BAN", 2115300, 134, 90, AO::kAbespeakAOResID, PalId::Default}}, @@ -1188,7 +1184,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { AnimId::MenuParamiteSpeak_DoIt, { "PARSPEAK.BAN", 888824, 233, 125, kParamiteSpeakResID, PalId::Default }, kNullAnimDetails }, { AnimId::MenuParamiteSpeak_Attack, { "PARSPEAK.BAN", 888936, 233, 125, kParamiteSpeakResID, PalId::Default }, kNullAnimDetails }, { AnimId::MenuParamiteSpeak_AllAYa, { "PARSPEAK.BAN", 889016, 233, 125, kParamiteSpeakResID, PalId::Default }, kNullAnimDetails }, - + {AnimId::MenuSligSpeak_Idle, { "SLGSPEAK.BAN", 1105688, 233, 125, kSligSpeakResID, PalId::Default}, kNullAnimDetails }, {AnimId::MenuSligSpeak_Hi, { "SLGSPEAK.BAN", 1106072, 233, 125, kSligSpeakResID, PalId::Default}, kNullAnimDetails }, {AnimId::MenuSligSpeak_HereBoy, { "SLGSPEAK.BAN", 1105600, 233, 125, kSligSpeakResID, PalId::Default}, kNullAnimDetails }, @@ -1214,7 +1210,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "HIGHLITE.BAN", 13900, 150, 65, kHighliteResID, PalId::Default }, {"HIGHLITE.BAN", 6140, 99, 43, AO::kHighliteAOResID, PalId::Default} }, - {AnimId::MenuDoor, + {AnimId::MenuDoor, { "STDOOR.BAN", 50212, 233, 125, kMenuDoorResID, PalId::Default }, { "DOOR.BAN", 41420, 134, 90, AO::kMenuDoorAOResID, PalId::Default}}, @@ -1222,7 +1218,6 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"ABEINTRO.BAN", 25888, 233, 125, kAbeintroResID, PalId::Default }, {"ABEINTRO.BAN", 37364, 134, 90, AO::kAbeintroAOResID, PalId::Default}}, - // TODO: check AO ResourceID's {AnimId::Slog_Idle, { "SLOG.BND", 96464, 121, 57, kDogbasicResID, PalId::Default}, {"SLOG.BND", 94456, 121, 57, AO::kDogbasicAOResID, PalId::Default}}, @@ -1304,13 +1299,15 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Background_Glukkon_Laugh, kNullAnimDetails, { "GLUKKON.BND", 46272, 68, 60, AO::kGlkbasicAOResID, PalId::Default} }, {AnimId::Background_Glukkon_KillHim1, kNullAnimDetails, { "GLUKKON.BND", 46128, 68, 60, AO::kGlkbasicAOResID, PalId::Default} }, {AnimId::Background_Glukkon_KillHim2, kNullAnimDetails, { "GLUKKON.BND", 46180, 68, 60, AO::kGlkbasicAOResID, PalId::Default} }, + // TODO: anim id name not confirmed + {AnimId::Background_Glukkon_Dying, kNullAnimDetails, { "GLUKKON.BND", 46232, 68, 60, AO::kGlkbasicAOResID, PalId::Default} }, {AnimId::BirdPortal_Sparks, {"PORTAL.BND", 4256, 32, 69, kPortliteResID, PalId::Default}, - {"PORTAL.BND", 3708, 31, 69, AO::kPortliteAOResID, PalId::Default}}, + {"PORTLITE.BAN", 3708, 31, 69, AO::kPortliteAOResID, PalId::Default}}, {AnimId::BirdPortal_Flash, {"PORTAL.BND", 13576, 145, 74, kPortlitResID, PalId::Default}, - {"PORTAL.BND", 13352, 145, 74, AO::kPortlitAOResID, PalId::Default}}, + {"PORTLIT.BAN", 13352, 145, 74, AO::kPortlitAOResID, PalId::Default}}, {AnimId::BirdPortal_TerminatorShrink, {"PORTAL.BND", 4168, 32, 18, kPortalTerminatorResID, PalId::Default}, @@ -1327,14 +1324,14 @@ constexpr CombinedAnimRecord kAnimRecords[] = { // the loading icons are apparently exactly the same except the maxW/maxH {AnimId::Loading_Icon, {"LOADING.BAN", 900, 150, 65, kLoadingResID, PalId::Default}, kNullAnimDetails}, {AnimId::Loading_Icon2, - {"LOADING.BAN", 900, 50, 38, kLoadingResID, PalId::Default}, + {"LOADING.BAN", 900, 50, 38, kLoadingResID, PalId::Default}, {"LOADING.BAN", 652, 50, 38, AO::kLoadingAOResID, PalId::Default}}, {AnimId::Vaporize_Particle, {"VAPOR.BAN", 5264, 61, 44, kVaporResID, PalId::Default}, kNullAnimDetails}, {AnimId::ShootingFire_Particle, {"BIGFLASH.BAN", 960, 86, 17, kBigflashResID, PalId::Default}, {"SLIG.BND", 804, 86, 17, AO::kBigflashAOResID, PalId::Default}}, - {AnimId::ChantOrb_Particle, + {AnimId::ChantOrb_Particle, {"OMMFLARE.BAN", 1632, 39, 21, kOmmflareResID, PalId::Default}, {"OMMFLARE.BAN", 1492, 38, 21, AO::kOmmflareAOResID, PalId::Default}}, {AnimId::SquibSmoke_Particle, @@ -1342,9 +1339,11 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"SQBSMK.BAN", 4108, 61, 44, AO::kSquibSmokeAOResID, PalId::Default}}, {AnimId::Explosion_Rocks, {"DEBRIS00.BAN", 6484, 71, 36, kDebrisID00ResID, PalId::Default}, kNullAnimDetails}, {AnimId::Explosion_Sticks, {"STICK.BAN", 1704, 49, 29, kStickGibResID, PalId::Default}, kNullAnimDetails}, + //{AnimId::Explosion_Skull, {"SKULL.BAN", 1704, 49, 29, kDebrisID00ResID, PalId::Default}, kNullAnimDetails }, // just a copy of stick.ban with a different res id and ban name + //{AnimId::Explosion_Bone, {"LEGBONE.BAN", 1976, 45, 25, kStickGibResID, PalId::Default}, kNullAnimDetails }, // unused bone gib anim {AnimId::Mine_Flash, {"MINE.BND", 400, 36, 8, kMineflshResID, PalId::Default}, - {"MINE.BND", 772, 36, 8, AO::kMineflshAOResID, PalId::Default}}, + {"MINEFLSH.BAN", 772, 36, 8, AO::kMineflshAOResID, PalId::Default}}, {AnimId::OptionChantOrb_Particle, {"STARTANM.BND", 4176, 92, 47, kOptionFlareResID, PalId::Default}, {"STARTANM.BND", 7152, 92, 47, AO::kOptionFlareAOResID, PalId::Default}}, @@ -1387,22 +1386,30 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::LiftBottomWheel_Necrum, {"NELIFT.BND", 8664, 69, 34, kLiftWheelsResID, PalId::Default}, kNullAnimDetails}, {AnimId::LiftTopWheel_Necrum, {"NELIFT.BND", 8700, 69, 34, kLiftWheelsResID, PalId::Default}, kNullAnimDetails}, - // TODO: figure out if this is the correct BAN/BND - {AnimId::ShrykullStart, {"SHRYPORT.BND", 82676, 123, 79, kShrmorphResID, PalId::Default}, kNullAnimDetails}, - {AnimId::ShrykullTransform, {"SHRYPORT.BND", 82712, 123, 79, kAbemorphResID, PalId::Default}, kNullAnimDetails}, - {AnimId::ShrykullDetransform, {"SHRYPORT.BND", 82824, 123, 79, kAbemorphResID, PalId::Default}, kNullAnimDetails}, + {AnimId::ShrykullStart, + {"SHRYPORT.BND", 82676, 123, 79, kShrmorphResID, PalId::Default}, + {"SHRMORPH.BAN", 92004, 121, 79, AO::kShrmorphAOResID, PalId::Default} + }, + {AnimId::ShrykullTransform, + {"SHRYPORT.BND", 82712, 123, 79, kShrmorphResID, PalId::Default}, + {"SHRMORPH.BAN", 92040, 121, 79, AO::kShrmorphAOResID, PalId::Default} + }, + {AnimId::ShrykullDetransform, + {"SHRYPORT.BND", 82824, 123, 79, kShrmorphResID, PalId::Default}, + {"SHRMORPH.BAN", 92152, 121, 79, AO::kShrmorphAOResID, PalId::Default} + }, {AnimId::NormalMudIcon, {"EMONORM.BAN", 1248, 54, 47, kNormaliconResID, PalId::Default}, kNullAnimDetails}, {AnimId::AngryMudIcon, {"EMOANGRY.BAN", 1076, 54, 47, kAngryiconResID, PalId::Default}, kNullAnimDetails}, {AnimId::HappyMudIcon, {"EMOHAP.BAN", 1252, 54, 47, kHappyiconResID, PalId::Default}, kNullAnimDetails}, {AnimId::SpotLight, {"SPOTLITE.BAN", 1536, 57, 32, kSpotliteResID, PalId::Default}, kNullAnimDetails}, - - // blood drop and blood are not the same animations. - // TODO: check if AO blood.ban is blood or blood drop - {AnimId::BloodDrop, {"BLOODROP.BAN", 308, 11, 7, kBloodropResID, PalId::Default}, kNullAnimDetails}, - {AnimId::Blood, kNullAnimDetails, { "BLOOD.BAN", 276, 6, 7, AO::kBloodropAOResID, PalId::Default} }, + {AnimId::BloodDrop, + {"BLOODROP.BAN", 308, 11, 7, kBloodropResID, PalId::Default}, + { "BLOODROP.BAN", 276, 6, 7, AO::kBloodropAOResID, PalId::Default} + }, + {AnimId::Bone, { "BTHROW.BND", 456, 15, 9, kBoneResID, PalId::Default}, kNullAnimDetails }, {AnimId::BoneBag_Idle, { "BONEBAG.BAN", 8748, 64, 60, kBoneBagResID_590, PalId::Default}, kNullAnimDetails }, {AnimId::BoneBag_HardHit, { "BONEBAG.BAN", 8708, 64, 60, kBoneBagResID_590, PalId::Default}, kNullAnimDetails }, @@ -1411,7 +1418,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Drill_Horizontal_On, { "DRILL.BAN", 6712, 65, 33, kDrillResID, PalId::Default}, kNullAnimDetails }, {AnimId::Drill_Vertical_Off, { "DRILL.BAN", 6676, 65, 33, kDrillResID, PalId::Default}, kNullAnimDetails }, {AnimId::Drill_Vertical_On, { "DRILL.BAN", 6688, 65, 33, kDrillResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Bullet_Shell, + {AnimId::Bullet_Shell, { "SHELL.BAN", 320, 5, 5u, kShellResID, PalId::Default}, { "SHELL.BAN", 308, 6, 5, AO::kShellAOResID, PalId::Default} }, @@ -1441,7 +1448,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Door_Brewery_Open, { "BRDOOR.BAN", 6236, 54, 71, kF2p3dorResID, PalId::Default}, kNullAnimDetails }, {AnimId::Door_Feeco_Closed, { "FDDOOR.BAN", 7636, 62, 70, kF2p3dorResID, PalId::Default}, kNullAnimDetails }, {AnimId::Door_Feeco_Open, { "FDDOOR.BAN", 7648, 62, 70, kF2p3dorResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Fire, + {AnimId::Fire, { "FIRE.BAN", 5156, 22, 21u, kHubFireResID, PalId::Default}, { "FIRE.BAN", 5072, 51, 24, AO::kHubFireAOResID, PalId::Default} }, @@ -1472,7 +1479,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::FinalTestDoor_Forest_Open, kNullAnimDetails, { "FDOOR.BND", 2048, 51, 27, AO::kRockdoorAOResID, PalId::Default}}, {AnimId::FinalTestDoor_Forest_Closed, kNullAnimDetails, { "FDOOR.BND", 2072, 51, 27, AO::kRockdoorAOResID, PalId::Default}}, - + {AnimId::Door_Desert_Open, kNullAnimDetails, { "DDOOR.BND", 3152, 52, 69, AO::kF2p3dorAOResID, PalId::Default}}, {AnimId::Door_Desert_Closed, kNullAnimDetails, { "DDOOR.BND", 3176, 52, 69, AO::kF2p3dorAOResID, PalId::Default}}, @@ -1491,14 +1498,14 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Electric_Wall, {"ELECWALL.BAN", 15384, 50, 80, kElecwallResID, PalId::Default}, {"ELECWALL.BAN", 14536, 50, 80, AO::kElecwallAOResID, PalId::Default}}, - {AnimId::Explosion, + {AnimId::AirExplosion, {"EXPLO2.BAN", 51156, 202, 91, kExplo2ResID, PalId::Default}, {"EXPLO2.BAN", 27376, 200, 91, AO::kExplo2AOResID, PalId::Default}}, - {AnimId::Explosion_Mine, + {AnimId::GroundExplosion, {"EXPLODE.BND", 51588, 214, 49, kBgexpldResID, PalId::Default}, - {"EXPLODE.BND", 51600, 214, 49, AO::kBgexpldAOResID, PalId::Default}}, + {"BGEXPLD.BAN", 51600, 214, 49, AO::kBgexpldAOResID, PalId::Default}}, - {AnimId::Explosion_Small, { "SMEXP.BAN", 14108, 99, 46, kSmallExplo2ResID, PalId::Default}, kNullAnimDetails }, + {AnimId::AirExplosion_Small, { "SMEXP.BAN", 14108, 99, 46, kSmallExplo2ResID, PalId::Default}, kNullAnimDetails }, {AnimId::FallingCrate_Falling, { "FALLBONZ.BAN", 8076, 86, 47, kF2rockResID, PalId::Default}, kNullAnimDetails }, {AnimId::FallingCrate_Waiting, { "FALLBONZ.BAN", 8100, 86, 47, kF2rockResID, PalId::Default}, kNullAnimDetails }, {AnimId::AE_FallingRock_Falling, { "FALLROCK.BAN", 4232, 64, 28, kF2rockResID, PalId::Default}, kNullAnimDetails }, @@ -1507,8 +1514,8 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::FallingMeat_Waiting, kNullAnimDetails, {"R1BARREL.BAN", 12148, 66, 42, AO::kF2rockAOResID, PalId::Default}}, {AnimId::FallingMeat_Falling, kNullAnimDetails, {"R1BARREL.BAN", 12136, 66, 42, AO::kF2rockAOResID, PalId::Default}}, - {AnimId::AO_FallingRock_Falling, kNullAnimDetails, {"F2ROCK.BAN", 11544, 76, 38, AO::kF2rockAOResID, PalId::Default}}, - {AnimId::AO_FallingRock_Waiting, kNullAnimDetails, {"F2ROCK.BAN", 11504, 76, 38, AO::kF2rockAOResID, PalId::Default}}, + {AnimId::AO_FallingRock_Falling, kNullAnimDetails, {"F2ROCK.BAN", 11504, 76, 38, AO::kF2rockAOResID, PalId::Default}}, + {AnimId::AO_FallingRock_Waiting, kNullAnimDetails, {"F2ROCK.BAN", 11544, 76, 38, AO::kF2rockAOResID, PalId::Default}}, {AnimId::Fart, { "EVILFART.BAN", 3532, 61, 39, kEvilFartResID, PalId::Default}, kNullAnimDetails }, {AnimId::Foot_Switch_Bonewerkz_Idle, { "BWTRIGGR.BAN", 548, 46, 11, kTriggerResID, PalId::Default}, kNullAnimDetails }, @@ -1516,26 +1523,27 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Foot_Switch_Industrial_Idle, { "TRIGGER.BAN", 568, 45, 11, kTriggerResID, PalId::Default}, kNullAnimDetails }, {AnimId::Foot_Switch_Industrial_Pressed, { "TRIGGER.BAN", 588, 45, 11, kTriggerResID, PalId::Default}, kNullAnimDetails }, {AnimId::Foot_Switch_Temple, kNullAnimDetails, { "TRIGGER.BAN", 744, 68, 15, AO::kTriggerAOResID, PalId::Default} }, + {AnimId::Foot_Switch_Temple_Pressed, kNullAnimDetails, { "TRIGGER.BAN", 756, 68, 15, AO::kTriggerAOResID, PalId::Default} }, {AnimId::Foot_Switch_Vault_Idle, { "VLTTRIGR.BAN", 972, 72, 18, kTriggerResID, PalId::Default}, kNullAnimDetails }, {AnimId::Foot_Switch_Vault_Pressed, { "VLTTRIGR.BAN", 992, 72, 18, kTriggerResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Grenade, - { "GRENADE.BAN", 1252, 17, 11, kGrenadeResID, PalId::Default}, + {AnimId::Grenade, + { "GTHROW.BND", 1252, 17, 11, kGrenadeResID, PalId::Default}, { "GRENADE.BAN", 1132, 17, 11, AO::kGrenadeAOResID, PalId::Default} }, {AnimId::BoomMachine_Button_Off, - { "GMACHINE.BND1", 1736, 26, 17, kR1buttonResID, PalId::Default}, - { "GMACHINE.BND1", 1600, 26, 17, AO::kR1buttonAOResID, PalId::Default} }, - {AnimId::BoomMachine_Button_On, - { "GMACHINE.BND1", 1708, 26, 17, kR1buttonResID, PalId::Default}, - { "GMACHINE.BND1", 1572, 26, 17, AO::kR1buttonAOResID, PalId::Default} + { "GMACHINE.BND", 1736, 26, 17, kR1buttonResID, PalId::Default}, + { "GMACHINE.BND", 1600, 26, 17, AO::kR1buttonAOResID, PalId::Default} }, + {AnimId::BoomMachine_Button_On, + { "GMACHINE.BND", 1708, 26, 17, kR1buttonResID, PalId::Default}, + { "GMACHINE.BND", 1572, 26, 17, AO::kR1buttonAOResID, PalId::Default} }, {AnimId::BoomMachine_Nozzle_Idle, - { "GMACHINE.BND1", 3700, 67, 36, kR1bpipeResID, PalId::Default}, - { "GMACHINE.BND1", 3616, 66, 36, AO::kR1bpipeAOResID, PalId::Default} + { "GMACHINE.BND", 3700, 67, 36, kR1bpipeResID, PalId::Default}, + { "GMACHINE.BND", 3616, 66, 36, AO::kR1bpipeAOResID, PalId::Default} }, {AnimId::BoomMachine_Nozzle_DropGrenade, - { "GMACHINE.BND1", 3672, 67, 36, kR1bpipeResID, PalId::Default}, - { "GMACHINE.BND1", 3588, 66, 36, AO::kR1bpipeAOResID, PalId::Default} }, + { "GMACHINE.BND", 3672, 67, 36, kR1bpipeResID, PalId::Default}, + { "GMACHINE.BND", 3588, 66, 36, AO::kR1bpipeAOResID, PalId::Default} }, {AnimId::HintFly, kNullAnimDetails, { "HINTFLY.BAN", 556, 10, 7, AO::kHintflyAOResID, PalId::Default} }, {AnimId::Honey, kNullAnimDetails, { "HONEY.BAN", 3680, 62, 126, AO::kHoneyAOResID, PalId::Default} }, {AnimId::Honey_Drip, kNullAnimDetails, { "HONEY.BAN", 3628, 62, 126, AO::kHoneyAOResID, PalId::Default} }, @@ -1543,12 +1551,11 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::HoneySack_Falling, kNullAnimDetails, { "HIVE.BND", 9336, 140, 38, AO::kP2c3HiveAOResID, PalId::Default} }, {AnimId::HoneySack_FallingToSmashed, kNullAnimDetails, { "HIVE.BND", 9280, 140, 38, AO::kP2c3HiveAOResID, PalId::Default} }, {AnimId::HoneySack_OnGround, kNullAnimDetails, { "HIVE.BND", 9360, 140, 38, AO::kP2c3HiveAOResID, PalId::Default} }, - {AnimId::Lift_Rope, { "ROPES.BAN", 748, 9, 16, kRopesResID, PalId::Default}, kNullAnimDetails }, - // TODO: more rope BAN'S? + {AnimId::AE_Rope, { "ROPES.BAN", 748, 9, 16, kRopesResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Meat, - { "MEAT.BAN", 488, 17, 9, kMeatResID, PalId::Default}, - { "MEAT.BAN", 500, 18, 9, AO::kMeatAOResID, PalId::Default} + {AnimId::Meat, + { "MTHROW.BND", 488, 17, 9, kMeatResID, PalId::Default}, + { "MTHROW.BND", 500, 18, 9, AO::kMeatAOResID, PalId::Default} }, {AnimId::MeatSack_Idle, { "MEATSACK.BAN", 15848, 93, 86, kMeatSackResID, PalId::Default}, @@ -1556,32 +1563,32 @@ constexpr CombinedAnimRecord kAnimRecords[] = { }, {AnimId::MeatSack_Hit, {"MEATSACK.BAN", 15888, 93, 86, kMeatSackResID, PalId::Default}, - { "D2ELUM.BAN", 15728, 92, 86, AO::kMeatSackAOResID, PalId::Default} }, - + { "D2ELUM.BAN", 15728, 92, 86, AO::kMeatSackAOResID, PalId::Default} }, // F2P04C07.CAM + {AnimId::MeatSaw_Idle, kNullAnimDetails, { "R1METSAW.BAN", 15200, 104, 36, AO::kMeatSawAOResID, PalId::Default} }, {AnimId::MeatSaw_Moving, kNullAnimDetails, { "R1METSAW.BAN", 15232, 104, 36, AO::kMeatSawAOResID, PalId::Default }}, // similar to the one above but this meatsaw anim has a higher rpm {AnimId::MeatSawMotor, kNullAnimDetails, {"R1METSAW.BAN", 15252, 104, 36, AO::kMeatSawAOResID, PalId::Default}}, - {AnimId::Mine, + {AnimId::Mine, { "MINE.BND", 784, 38, 13, kLandmineResID, PalId::Default }, { "MINE.BND", 748, 38, 13, AO::kLandmineAOResID, PalId::Default } }, - {AnimId::MotionDetector_Flare, + {AnimId::MotionDetector_Flare, { "MFLARE.BAN", 1736, 55, 22, kMflareResID, PalId::Default}, { "MFLARE.BAN", 1108, 32, 22, AO::kMflareAOResID, PalId::Default} }, - {AnimId::MotionDetector_Laser, + {AnimId::MotionDetector_Laser, { "MOTION.BAN", 23280, 37, 60, kMotionResID, PalId::Default}, { "MOTION.BAN", 23660, 37, 60, AO::kMotionAOResID, PalId::Default} }, - {AnimId::MovingBomb, + {AnimId::MovingBomb, { "MOVEBOMB.BAN", 17548, 76, 30, kMovebombResID, PalId::Default}, - { "MOVEBOMB.BAN", 17140, 76, 30, AO::kMovebombAOResID, PalId::Default} + { "D1MBOMB.BAN", 17140, 76, 30, AO::kMovebombAOResID, PalId::Default} }, - {AnimId::ParamiteWeb, + {AnimId::ParamiteWeb, { "WEB.BAN", 148, 5, 16, kWebResID, PalId::Default}, { "WEB.BAN", 88, 3, 16, AO::kWebAOResID, PalId::Default} }, @@ -1593,20 +1600,20 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Pullring_Farms_UseBegin, kNullAnimDetails, { "R1PULRNG.BAN", 4872, 33, 35, AO::kPullringAOResID, PalId::Default} }, {AnimId::Pullring_Farms_UseEnd, kNullAnimDetails, { "R1PULRNG.BAN", 4904, 33, 35, AO::kPullringAOResID, PalId::Default} }, - {AnimId::Rock, + {AnimId::Rock, { "RTHROW.BND", 488, 17, 9, kAberockResID, PalId::Default}, { "RTHROW.BND", 500, 18, 9, AO::kAberockAOResID, PalId::Default} }, - {AnimId::RockSack_Idle, + {AnimId::RockSack_Idle, { "ROCKBAG.BAN", 29748, 71, 60u, kP2c2bagResID, PalId::Default}, - { "E1BAG.BAN", 13756, 71, 60, AO::kP2c2bagAOResID, PalId::Default} + { "E1P02C03.CAM", 13756, 71, 60, AO::kP2c2bagAOResID, PalId::Default} }, - {AnimId::RockSack_SoftHit, + {AnimId::RockSack_SoftHit, { "ROCKBAG.BAN", 29772, 71, 60u, kP2c2bagResID, PalId::Default}, - { "E1BAG.BAN", 13780, 71, 60, AO::kP2c2bagAOResID, PalId::Default} }, + { "E1P02C03.CAM", 13780, 71, 60, AO::kP2c2bagAOResID, PalId::Default} }, {AnimId::RockSack_HardHit, { "ROCKBAG.BAN", 29700, 71, 60u, kP2c2bagResID, PalId::Default}, - { "E1BAG.BAN", 13708, 71, 60, AO::kP2c2bagAOResID, PalId::Default} }, + { "E1P02C03.CAM", 13708, 71, 60, AO::kP2c2bagAOResID, PalId::Default} }, {AnimId::Rope_R1, kNullAnimDetails, { "R1ROPES.BAN", 636, 4, 16, AO::kRopesAOResID, PalId::Default} }, {AnimId::Rope_Lines, kNullAnimDetails, { "ROPES.BAN", 680, 5, 16, AO::kRopesAOResID, PalId::Default} }, @@ -1616,10 +1623,13 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Security_Claw_Lower_Idle, kNullAnimDetails, { "F2EYEORB.BAN", 22468, 152, 31, AO::kF2eyeorbAOResID, PalId::Default} }, {AnimId::Security_Claw_Upper_Rotating, kNullAnimDetails, { "F2EYEORB.BAN", 22480, 152, 31, AO::kF2eyeorbAOResID, PalId::Default} }, {AnimId::Security_Claw_Upper_NoRotation, kNullAnimDetails, { "F2EYEORB.BAN", 22616, 152, 31, AO::kF2eyeorbAOResID, PalId::Default} }, - {AnimId::Security_Door, kNullAnimDetails, { "R1SDOS.BAN", 976, 70, 19, AO::kR1sdosAOResID_6027, PalId::Default} }, - {AnimId::Security_Door_Idle, { "SECDOOR.BAN", 1512, 70, 19u, kR1sdosResID_6027, PalId::Default}, kNullAnimDetails }, - {AnimId::Security_Door_Speak, { "SECDOOR.BAN", 1528, 70, 19u, kR1sdosResID_6027, PalId::Default}, kNullAnimDetails }, - {AnimId::Security_Orb, + {AnimId::Security_Door_Idle, + { "SECDOOR.BAN", 1512, 70, 19u, kR1sdosResID_6027, PalId::Default}, + { "R1SDOS.BAN", 976, 70, 19, AO::kR1sdosAOResID_6027, PalId::Default} }, + {AnimId::Security_Door_Speak, + { "SECDOOR.BAN", 1528, 70, 19u, kR1sdosResID_6027, PalId::Default}, + { "R1SDOS.BAN", 992, 70, 19, AO::kR1sdosAOResID_6027, PalId::Default} }, + {AnimId::Security_Orb, { "MAIMORB.BAN", 2228, 53, 28u, kMaimGameResID, PalId::Default}, { "F2MAMORB.BAN", 10864, 53, 28, AO::kMaimGameAOResID, PalId::Default} }, @@ -1634,13 +1644,23 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::SlapLock_Punched, { "GHOSTTRP.BAN", 7056, 58, 44, kGhostTrpResID_1053, PalId::Default}, kNullAnimDetails }, {AnimId::SlapLock_Shaking, { "GHOSTTRP.BAN", 6976, 58, 44, kGhostTrpResID_1053, PalId::Default}, kNullAnimDetails }, - {AnimId::Sparks, { "SPARKS.BAN", 808, 19, 11, kSparksResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Star, kNullAnimDetails, { "STAR.BAN", 748, 20, 12, AO::kBGStarAOResID, PalId::Default} }, + // smoother version of ChantOrb_Particle_Small with more frames + {AnimId::AE_ZapSpark, { "SPARKS.BAN", 808, 19, 11, kSparksResID, PalId::Default}, kNullAnimDetails }, + {AnimId::Star, + { "STAR.BAN", 772, 19, 12, kBGStarResID, PalId::Default}, // STAR.BAN doesn't exist in any AE level + { "STAR.BAN", 748, 20, 12, AO::kBGStarAOResID, PalId::Default} }, {AnimId::Status_Light_Green, { "STATUSLT.BAN", 240, 14, 9u, kStatusLightResID, PalId::Default}, kNullAnimDetails }, {AnimId::Status_Light_Red, { "STATUSLT.BAN", 252, 14, 9u, kStatusLightResID, PalId::Default}, kNullAnimDetails }, - {AnimId::Stone_Ball, kNullAnimDetails, { "F2STNBAL.BAN", 15596, 131, 64, AO::kF2stnbalAOResID, PalId::Default} }, + // It appears that ROLLROCK.BAN is only present in the demo version of AE + {AnimId::Stone_Ball, + { "ROLLROCK.BAN", 15728, 132, 64, kF2stnbalResID, PalId::Default}, + { "F2STNBAL.BAN", 15596, 131, 64, AO::kF2stnbalAOResID, PalId::Default} }, + {AnimId::Stone_Ball_Rolling, + { "ROLLROCK.BAN", 15740, 132, 64, kF2stnbalResID, PalId::Default}, + { "F2STNBAL.BAN", 15608, 131, 64, AO::kF2stnbalAOResID, PalId::Default} + }, {AnimId::Stone_Ball_Stopper, kNullAnimDetails, { "F2STNSCK.BAN", 1256, 106, 73, AO::kF2stnsckAOResID, PalId::Default} }, {AnimId::Swinging_Ball_Fast, kNullAnimDetails, { "F2ZBALL.BAN", 72288, 143, 182, AO::kF2zballAOResID, PalId::Default} }, {AnimId::Swinging_Ball_Normal, kNullAnimDetails, { "F2ZBALL.BAN", 72172, 143, 182, AO::kF2zballAOResID, PalId::Default} }, @@ -1674,8 +1694,11 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {AnimId::Lines_Switch_Pull_Right, kNullAnimDetails, {"L1SWITCH.BAN", 5488, 66, 41, AO::kSwitchAOResID, PalId::Default}}, {AnimId::Lines_Switch_Pull_Release_Right, kNullAnimDetails, {"L1SWITCH.BAN", 5540, 66, 41, AO::kSwitchAOResID, PalId::Default}}, - {AnimId::TimedMine_Activated, { "BOMB.BND", 848, 35, 17, kBombResID, PalId::Default}, kNullAnimDetails }, - {AnimId::TimedMine_Idle, + {AnimId::TimedMine_Activated, + { "BOMB.BND", 848, 35, 17, kBombResID, PalId::Default}, + { "BOMB.BND", 804, 35, 17, AO::kBombAOResID, PalId::Default} + }, + {AnimId::TimedMine_Idle, { "BOMB.BND", 836, 35, 17, kBombResID, PalId::Default}, { "BOMB.BND", 792, 35, 17, AO::kBombAOResID, PalId::Default} }, @@ -1722,7 +1745,7 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { "UXB.BND", 8120, 59, 19, kUXBResID, PalId::Default}, { "UXB.BND", 7812, 59, 19, AO::kUXBAOResID, PalId::Default} }, {AnimId::UXB_Disabled, - { "UXB.BND", 8192, 59, 19, kUXBResID, PalId::Default}, + { "UXB.BND", 8192, 59, 19, kUXBResID, PalId::Default}, { "UXB.BND", 7884, 59, 19, AO::kUXBAOResID, PalId::Default} }, {AnimId::Well_Leaf, {"WELLLEAF.BAN", 476, 16, 11, kWellLeafResID, PalId::Default}, @@ -1737,20 +1760,35 @@ constexpr CombinedAnimRecord kAnimRecords[] = { {"SPLINE.BAN", 288, 14, 9, kSplineResID, PalId::Default}, {"SPLINE.BAN", 240, 9, 9, AO::kSplineAOResID, PalId::Default}}, - {AnimId::Zap_Sparks, + {AnimId::ChantOrb_Particle_Small, {"OMMFLARE.BAN", 1672, 39, 21, kOmmflareResID, PalId::Default}, {"OMMFLARE.BAN", 1532, 38, 21, kOmmflareResID, PalId::Default}}, - {AnimId::GoldGlow, kNullAnimDetails, {"GOLDGLOW.BAN", 236, 19, 15, AO::kGoldGlowAOResID_6011, PalId::Default}}, - {AnimId::GreenGlow, kNullAnimDetails, {"GRENGLOW.BAN", 96, 5, 6, AO::kGreenGlowAOResID_6010, PalId::Default}}, - {AnimId::FlintGlow, kNullAnimDetails, {"FLNT.BAN", 756, 59, 54, AO::kFlintGlowAOResID_6028, PalId::Default}}, + {AnimId::GoldGlow, + {"GOLDGLOW.BAN", 356, 33, 15, kGoldGlowResID_6011, PalId::Default}, + {"GOLDGLOW.BAN", 236, 19, 15, AO::kGoldGlowAOResID_6011, PalId::Default}}, - {AnimId::GreenDoorLight, kNullAnimDetails, {"HUBLIGHT.BAN", 448, 20, 11, AO::kGreenDoorLightAOResID_6031, PalId::Default}}, - {AnimId::GreenHubLight, kNullAnimDetails, {"HUBLIGHT.BAN", 460, 20, 11, AO::kGreenDoorLightAOResID_6031, PalId::Default}}, + {AnimId::GreenGlow, + {"GRENGLOW.BAN", 112, 8, 6, kGreenGlowResID_6010, PalId::Default}, + {"GRENGLOW.BAN", 96, 5, 6, AO::kGreenGlowAOResID_6010, PalId::Default}}, - {AnimId::RedDoorLight, kNullAnimDetails, {"HUBRED.BAN", 460, 20, 11, AO::kRedDoorLightAOResID_6032, PalId::Default}}, - {AnimId::RedHubLight, kNullAnimDetails, {"HUBRED.BAN", 448, 20, 11, AO::kRedDoorLightAOResID_6032, PalId::Default}}, + {AnimId::FlintGlow, kNullAnimDetails, {"FLNTGLOW.BAN", 756, 59, 54, AO::kFlintGlowAOResID_6028, PalId::Default}}, + {AnimId::GreenDoorLight, + {"HUBLIGHT.BAN", 244, 16, 10, kGreenDoorLightResID_6031, PalId::Default}, + {"HUBLIGHT.BAN", 448, 20, 11, AO::kGreenDoorLightAOResID_6031, PalId::Default}}, + + {AnimId::GreenHubLight, + {"HUBLIGHT.BAN", 256, 16, 10, kGreenDoorLightResID_6031, PalId::Default}, + {"HUBLIGHT.BAN", 460, 20, 11, AO::kGreenDoorLightAOResID_6031, PalId::Default}}, + + {AnimId::RedDoorLight, + {"HUBRED.BAN", 256, 16, 10, kRedDoorLightResID_6032, PalId::Default}, + {"HUBRED.BAN", 460, 20, 11, AO::kRedDoorLightAOResID_6032, PalId::Default}}, + + {AnimId::RedHubLight, + {"HUBRED.BAN", 244, 16, 10, kRedDoorLightResID_6032, PalId::Default}, + {"HUBRED.BAN", 448, 20, 11, AO::kRedDoorLightAOResID_6032, PalId::Default}}, {AnimId::FlintLock_Hammers_Activating, kNullAnimDetails, {"F2FLINT.BAN", 11848, 125, 59, AO::kFlintLockAOResID, PalId::Default}}, {AnimId::FlintLock_Hammers_Disabled, kNullAnimDetails, {"F2FLINT.BAN", 11836, 125, 59, AO::kFlintLockAOResID, PalId::Default}}, @@ -1834,10 +1872,13 @@ constexpr CombinedAnimRecord kAnimRecords[] = { { AnimId::Elum_Knockback, kNullAnimDetails, {"ELMKNBK.BAN", 21280, 169, 169, AO::kElmnknbkAOResID, PalId::Default} }, + { AnimId::Scoopz, {"SCOOPZ.BAN", 31616, 59, 200, kScoopzResID, PalId::Default}, kNullAnimDetails }, + // search for "Animation test code" in Abe.cpp and uncomment the code below to activate the anim tester //////////////////////////////////////////////////////////////////////////////////////////////////// + {AnimId::Anim_Tester, - {"SLIGZ.BND", 2832, 126, 44, kZflashResID, PalId::Default}, + {"SCOOPZ.BAN", 31616, 59, 200, kScoopzResID, PalId::Default}, {"RLIFT.BND", 24240, 124, 37, AO::kLiftWheelsAOResID, PalId::Default} }, //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1879,7 +1920,7 @@ void FrameTableOffsetExists(int frameTableOffset, bool isAe, int maxW, int maxH) } // special handling for some weird OG behavior (see Explosion.cpp line 184) - if (entry.mId == AnimId::Explosion_Small) + if (entry.mId == AnimId::AirExplosion_Small) { return; } @@ -1897,7 +1938,7 @@ void FrameTableOffsetExists(int frameTableOffset, bool isAe, int maxW, int maxH) LOG_INFO("couldn't find AnimId for framtableoffset: " << frameTableOffset << " maxW " << maxW << " maxH " << maxH); } -void FrameTableOffsetExists(int frameTableOffset, bool isAe) +bool FrameTableOffsetExists(int frameTableOffset, bool isAe) { for (const auto& entry : kAnimRecords) { @@ -1905,18 +1946,21 @@ void FrameTableOffsetExists(int frameTableOffset, bool isAe) { if (entry.mAEData.mFrameTableOffset == frameTableOffset) { - return; + return true; } } else { if (entry.mAOData.mFrameTableOffset == frameTableOffset) { - return; + return true; } } } + LOG_INFO("couldn't find AnimId for framtableoffset: " << frameTableOffset); + + return false; } static const PalRecord PalRec(bool isAe, PalId toFind) @@ -1975,6 +2019,56 @@ const AnimRecord AnimRec(AnimId toFind) return AnimRec(true, toFind); } +const AnimRecord AnimRecFrameTable(int frameTableOffset, int resourceId, bool isAe) +{ + for (const auto& entry : kAnimRecords) + { + if (isAe) + { + if (entry.mAEData.mFrameTableOffset == frameTableOffset && entry.mAEData.mResourceId == resourceId) + { + const AnimDetails& data = entry.mAEData; + return AnimRecord{ entry.mId, data.mBanName, data.mFrameTableOffset, data.mMaxW, data.mMaxH, data.mResourceId, data.mPalOverride }; + } + } + else + { + if (entry.mAOData.mFrameTableOffset == frameTableOffset && entry.mAOData.mResourceId == resourceId) + { + const AnimDetails& data = entry.mAOData; + return AnimRecord{ entry.mId, data.mBanName, data.mFrameTableOffset, data.mMaxW, data.mMaxH, data.mResourceId, data.mPalOverride }; + } + } + } + + static const AnimRecord Empty = {}; + return Empty; +} + +bool AnimRecExists(bool isAe, AnimId toFind) +{ + for (const CombinedAnimRecord& anim : kAnimRecords) + { + if (anim.mId == toFind && (((isAe) ? anim.mAEData : anim.mAOData)).mBanName != nullptr) + { + return true; + } + } + return false; +} + +std::vector GetAnimRecords() +{ + std::vector table; + + for (CombinedAnimRecord anim : kAnimRecords) + { + table.push_back(anim); + } + + return table; +} + namespace AO { const PalRecord PalRec(PalId toFind) diff --git a/Source/AliveLibCommon/AnimResources.hpp b/Source/AliveLibCommon/AnimResources.hpp index 3cae57b15..d4f60c311 100644 --- a/Source/AliveLibCommon/AnimResources.hpp +++ b/Source/AliveLibCommon/AnimResources.hpp @@ -1,6 +1,7 @@ #pragma once #include "../AliveLibCommon/FunctionFwd.hpp" + enum class AnimId { None = 0, @@ -65,7 +66,6 @@ enum class AnimId Mudokon_RollingKnockForward, Mudokon_LiftGrabIdle, Mudokon_LiftUseDown, - Mudokon_AO_LiftUse, Mudokon_LiftGrabEnd, Mudokon_LiftGrabBegin, Mudokon_LiftUseUp, @@ -102,6 +102,7 @@ enum class AnimId Bat_Flying, Bat, + Bat_Unknown, Bee_Swarm, Fleech_Climb, @@ -124,23 +125,23 @@ enum class AnimId Fleech_DeathByFalling, Fleech_Crawl, - Flying_Slig_Idle, - Flying_Slig_Move_Horizontal, - Flying_Slig_Idle_Turn_Around, - Flying_Slig_Move_Down, - Flying_Slig_Move_Down_Turn_Around, - Flying_Slig_Move_Up, - Flying_Slig_Move_Up_Turn_Around, - Flying_Slig_Pull_Lever, - Flying_Slig_Speak, - Flying_Slig_Possession, - Flying_Slig_Move_Horizontal_End, - Flying_Slig_Move_Up_Start, - Flying_Slig_Move_Horizontal_To_Down, - Flying_Slig_Move_Up_To_Horizontal, - Flying_Slig_Move_Down_To_Horizontal, - Flying_Slig_Turn_Quick, - Flying_Slig_Idle_To_Horizontal, + FlyingSlig_Idle, + FlyingSlig_MoveHorizontal, + FlyingSlig_IdleTurnAround, + FlyingSlig_MoveDown, + FlyingSlig_MoveDownTurnAround, + FlyingSlig_MoveUp, + FlyingSlig_MoveUpTurnAround, + FlyingSlig_PullLever, + FlyingSlig_Speak, + FlyingSlig_Possession, + FlyingSlig_MoveHorizontalEnd, + FlyingSlig_MoveUpStart, + FlyingSlig_MoveHorizontalToDown, + FlyingSlig_MoveUpToHorizontal, + FlyingSlig_MoveDownToHorizontal, + FlyingSlig_TurnQuick, + FlyingSlig_IdleToHorizontal, FlyingSlig_BeginDownMovement, FlyingSlig_EndDownMovement, FlyingSlig_DownKnockback, @@ -292,7 +293,6 @@ enum class AnimId Mudokon_Run, Mudokon_RunSlideStop, Mudokon_RunSlideTurn, - Mudokon_Run_End_C, Mudokon_RunJumpMid, Mudokon_RunJumpBegin, Mudokon_CrouchScrub, @@ -315,7 +315,6 @@ enum class AnimId Mudokon_CrouchChant, Mudokon_CrouchChantToStruggle, Mudokon_DunnoMid, - kNullAnimDetails, Mudokon_AO_M_15_Null, Mudokon_CrouchSpeak1, Mudokon_CrouchSpeak2, @@ -356,14 +355,9 @@ enum class AnimId Mudokon_Idle_RubEyes, Mudokon_Idle_StretchArms, Mudokon_Idle_Yawn, - Mudokon_AO_ToShrykull, - Mudokon_AO_ShrykullEnd, - Mudokon_AO_Unknown1, - Mudokon_AO_Unknown2, - Mudokon_AO_Unknown3, - Mudokon_AO_Unknown4, + Mudokon_AO_HoistBegin_Long, + Mudokon_AO_LandSoft_Long, Mudokon_Sneak, - Mudokon_Sneak_End, Mudokon_WalkToSneak, Mudokon_StandToCrouch, Mudokon_Speak1, @@ -374,7 +368,6 @@ enum class AnimId Mudokon_WalkBegin, Mudokon_WalkToIdle, Mudokon_MidWalkToIdle, - Mudokon_AO_Null, Mudokon_StartChisel, Mudokon_StopChisel, Mudokon_WalkToRun, @@ -418,7 +411,6 @@ enum class AnimId Paramite_Running, Paramite_Hop, Paramite_PreHiss, - Paramite_Squawk, Paramite_AllOYaGameSpeakBegin, Paramite_GameSpeakEnd, Paramite_GameSpeakBegin, @@ -474,24 +466,24 @@ enum class AnimId Scrab_AttackSpin, Scrab_ScrabBattleAnim, - Crawling_Slig_Idle, - Crawling_Slig_UsingButton, - Crawling_Slig_WakingUp, - Crawling_Slig_Crawling, - Crawling_Slig_StartFalling, - Crawling_Slig_Falling, - Crawling_Slig_Landing, - Crawling_Slig_ToShakingToIdle, - Crawling_Slig_Speaking, - Crawling_Slig_Snoozing, - Crawling_Slig_PushingWall, - Crawling_Slig_TurnAround, - Crawling_Slig_Shaking, - Crawling_Slig_Empty, - Crawling_Slig_ShakingToIdle, - Crawling_Slig_EndCrawling, - Crawling_Slig_IdleToPushingWall, - Crawling_Slig_EndPushingWall, + Crawling_Slig_Idle, + Crawling_Slig_UsingButton, + Crawling_Slig_WakingUp, + Crawling_Slig_Crawling, + Crawling_Slig_StartFalling, + Crawling_Slig_Falling, + Crawling_Slig_Landing, + Crawling_Slig_ToShakingToIdle, + Crawling_Slig_Speaking, + Crawling_Slig_Snoozing, + Crawling_Slig_PushingWall, + Crawling_Slig_TurnAround, + Crawling_Slig_Shaking, + Crawling_Slig_Empty, + Crawling_Slig_ShakingToIdle, + Crawling_Slig_EndCrawling, + Crawling_Slig_IdleToPushingWall, + Crawling_Slig_EndPushingWall, Slig_Beat, Slig_OutToFall, @@ -613,7 +605,6 @@ enum class AnimId Slog_MoveHeadUpwards, Slog_StartFastBarking, Slog_AngryBark, - Slog_BROKEN_J, Slog_Dying, Slog_Eating, Slog_Fall, @@ -645,6 +636,7 @@ enum class AnimId Background_Glukkon_Laugh, Background_Glukkon_KillHim1, Background_Glukkon_KillHim2, + Background_Glukkon_Dying, BellHammer_Idle, BellHammer_Smashing, ObjectShadow, @@ -698,7 +690,6 @@ enum class AnimId PullRingRope_Idle, PullRingRope_UseEnd, PullRingRope_UseBegin, - Blood, Bone, BoneBag_Idle, BoneBag_HardHit, @@ -736,6 +727,8 @@ enum class AnimId SquibSmoke_Particle, Explosion_Rocks, Explosion_Sticks, + //Explosion_Skull, + //Explosion_Bone, Mine_Flash, OptionChantOrb_Particle, ShootingZFire_Particle, @@ -745,7 +738,7 @@ enum class AnimId WaterDrop, BrewMachine_Button, CrawlingSligLocker_Closed, - CrawlingSligLocker_Open, + CrawlingSligLocker_Open, LiftPlatform_Mines, LiftBottomWheel_Mines, LiftTopWheel_Mines, @@ -791,9 +784,9 @@ enum class AnimId Dove_Idle, Dove_Flying, Electric_Wall, - Explosion, - Explosion_Mine, - Explosion_Small, + AirExplosion, + GroundExplosion, + AirExplosion_Small, FallingCrate_Falling, FallingCrate_Waiting, AE_FallingRock_Falling, @@ -808,6 +801,7 @@ enum class AnimId Foot_Switch_Industrial_Idle, Foot_Switch_Industrial_Pressed, Foot_Switch_Temple, + Foot_Switch_Temple_Pressed, Foot_Switch_Vault_Idle, Foot_Switch_Vault_Pressed, Grenade, @@ -822,7 +816,7 @@ enum class AnimId HoneySack_Falling, HoneySack_FallingToSmashed, HoneySack_OnGround, - Lift_Rope, + AE_Rope, Meat, MeatSack_Idle, MeatSack_Hit, @@ -851,7 +845,6 @@ enum class AnimId Security_Claw_Lower_Idle, Security_Claw_Upper_Rotating, Security_Claw_Upper_NoRotation, - Security_Door, Security_Door_Idle, Security_Door_Speak, Security_Orb, @@ -864,11 +857,12 @@ enum class AnimId SlapLock_Initiate, SlapLock_Punched, SlapLock_Shaking, - Sparks, + AE_ZapSpark, Star, Status_Light_Green, Status_Light_Red, Stone_Ball, + Stone_Ball_Rolling, Stone_Ball_Stopper, Swinging_Ball_Fast, Swinging_Ball_Normal, @@ -927,7 +921,7 @@ enum class AnimId Work_Wheel_Turning, Zap_Line_Blue, Zap_Line_Red, - Zap_Sparks, + ChantOrb_Particle_Small, GoldGlow, GreenGlow, FlintGlow, @@ -953,6 +947,7 @@ enum class AnimId LiftTopWheel_Forest, LiftTopWheel_Desert, LiftTopWheel_Desert2, + Scoopz, Anim_Tester, // For animation testing with the TestAnimation.cpp, ignore this. }; @@ -1037,9 +1032,12 @@ struct CombinedAnimRecord final const PalRecord PalRec(PalId toFind); const AnimRecord AnimRec(AnimId toFind); +bool AnimRecExists(bool isAe, AnimId toFind); +std::vector GetAnimRecords(); +const AnimRecord AnimRecFrameTable(int frameTableOffset, int resourceId, bool isAe); const BgAnimRecord BgAnimRec(s32 toFind); void FrameTableOffsetExists(int frameTableOffset, bool isAe, int maxW, int maxH); -void FrameTableOffsetExists(int frameTableOffset, bool isAe); +bool FrameTableOffsetExists(int frameTableOffset, bool isAe); namespace AO { @@ -1381,6 +1379,7 @@ enum AEResourceID kCrawlingSligButtonResID_1057 = 1057, kDebrisID00ResID = 1105, kTubeResID = 1200, + kScoopzResID = 1205, kTestanimResID = 2000, kF2zballResID = 2001, // remove? @@ -1418,11 +1417,15 @@ enum AEResourceID kR1sdoorResID = 6006, kR1buttonResID = 6008, kR1bpipeResID = 6009, + kGreenGlowResID_6010, + kGoldGlowResID_6011 = 6011, kBayrollResID_6013 = 6013, kBrewButtonResID_6016 = 6016, kEvilFartResID = 6017, kR1sdosResID_6027 = 6027, - + kFlintGlowResID_6028 = 6028, + kGreenDoorLightResID_6031 = 6031, + kRedDoorLightResID_6032 = 6032, kTwnkleResID = 7012, kControlsResID = 8000, diff --git a/Source/AliveLibCommon/CMakeLists.txt b/Source/AliveLibCommon/CMakeLists.txt index ab6b278dc..b65d6ce75 100644 --- a/Source/AliveLibCommon/CMakeLists.txt +++ b/Source/AliveLibCommon/CMakeLists.txt @@ -35,6 +35,8 @@ SET(AliveLibSrcCommon PSXMDECDecoder.h Psx_common.hpp W32CrashHandler.hpp + ExternalAssets.hpp + ExternalAssets.cpp ) add_library(AliveLibCommon ${AliveLibSrcCommon}) diff --git a/Source/AliveLibCommon/ExternalAssets.cpp b/Source/AliveLibCommon/ExternalAssets.cpp new file mode 100644 index 000000000..8e8010104 --- /dev/null +++ b/Source/AliveLibCommon/ExternalAssets.cpp @@ -0,0 +1,38 @@ +#include "ExternalAssets.hpp" +#include + +// We're creating a map for all render commands. This is because we need to tie them to the original +// polygon data. This gets checked inside the renderer, and if a polygon has a render command tied to it, +// it will use that render command instead of the original poly render pipeline. (PSX emu stuff) +// The only reason that we're doing this is because we can't break ABI compatibility at the moment. +std::map g_RenderCommands; + +CustomRenderSpriteFormat* CustomRender_GetCommand(void* ptr) +{ + auto it = g_RenderCommands.find(ptr); + if (it != g_RenderCommands.end()) + { + return &it->second; + } + return nullptr; +} + +void CustomRender_AddCommand(void* ptr, CustomRenderSpriteFormat command) +{ + command.originalPoly = ptr; + g_RenderCommands[ptr] = command; +} + +void CustomRender_RemoveCommand(void* ptr) +{ + auto it = g_RenderCommands.find(ptr); + if (it != g_RenderCommands.end()) + { + g_RenderCommands.erase(it); + } +} + +void CustomRender_ClearCommands() +{ + g_RenderCommands.clear(); +} \ No newline at end of file diff --git a/Source/AliveLibCommon/ExternalAssets.hpp b/Source/AliveLibCommon/ExternalAssets.hpp new file mode 100644 index 000000000..edc069674 --- /dev/null +++ b/Source/AliveLibCommon/ExternalAssets.hpp @@ -0,0 +1,21 @@ +struct CustomRenderSpriteFormat +{ + void* originalPoly; + int x; + int y; + unsigned char r; + unsigned char g; + unsigned char b; + float scale; + bool flip; + void* origPtr; + int resource_id; + int frametable_offset; + unsigned int frame; + bool emissive; +}; + +CustomRenderSpriteFormat* CustomRender_GetCommand(void* ptr); +void CustomRender_AddCommand(void* ptr, CustomRenderSpriteFormat command); +void CustomRender_RemoveCommand(void* ptr); +void CustomRender_ClearCommands(); \ No newline at end of file diff --git a/Source/AliveLibCommon/Masher.cpp b/Source/AliveLibCommon/Masher.cpp index 60f895d37..d3f6a00e2 100644 --- a/Source/AliveLibCommon/Masher.cpp +++ b/Source/AliveLibCommon/Masher.cpp @@ -1071,6 +1071,94 @@ void Masher::VideoFrameDecode_4E6C60(u8* pPixelBuffer) } } +void Masher::VideoFrameDecode_Raw(u8* pPixelBuffer) +{ + if (!field_61_bHasVideo) + { + return; + } + + ++field_6C_frame_num; + + // if (!(field_6C_frame_num % field_14_video_header.field_14_key_frame_rate)) + { + // return; + } + + //if (field_6C_frame_num < field_2C_audio_header.field_10_num_frames_interleave) + { + // return; + } + + // if (field_68_frame_number < field_2C_audio_header.field_10_num_frames_interleave) + { + // return; + } + + const s32 blocksX = field_58_macro_blocks_x; + const s32 blocksY = field_5C_macro_blocks_y; + if (blocksX <= 0 || field_5C_macro_blocks_y <= 0) + { + return; + } + + const s32 quantScale = decode_bitstream((u16*)field_40_video_frame_to_decode, field_44_decoded_frame_data_buffer); + + Populate_Y_C_Tables(quantScale); + + int16_t* bitstreamCurPos = reinterpret_cast(field_44_decoded_frame_data_buffer); + int16_t* block1Output = static_cast(field_8C_macro_block_buffer); + + s32 xoff = 0; + for (s32 xBlock = 0; xBlock < blocksX; xBlock++) + { + s32 yoff = 0; + for (s32 yBlock = 0; yBlock < blocksY; yBlock++) + { + const s32 dataSizeBytes = field_90_64_or_0 * 2; // Convert to byte count 64*4=256 + + int16_t* afterBlock1Ptr = RunLengthToBlock(bitstreamCurPos, block1Output, 0); + idct(block1Output, Cr_block); + int16_t* block2Output = dataSizeBytes + block1Output; + + int16_t* afterBlock2Ptr = RunLengthToBlock(afterBlock1Ptr, block2Output, 0); + idct(block2Output, Cb_block); + int16_t* block3Output = dataSizeBytes + block2Output; + + int16_t* afterBlock3Ptr = RunLengthToBlock(afterBlock2Ptr, block3Output, 1); + idct(block3Output, Y1_block); + int16_t* block4Output = dataSizeBytes + block3Output; + + int16_t* afterBlock4Ptr = RunLengthToBlock(afterBlock3Ptr, block4Output, 1); + idct(block4Output, Y2_block); + int16_t* block5Output = dataSizeBytes + block4Output; + + int16_t* afterBlock5Ptr = RunLengthToBlock(afterBlock4Ptr, block5Output, 1); + idct(block5Output, Y3_block); + int16_t* block6Output = dataSizeBytes + block5Output; + + bitstreamCurPos = RunLengthToBlock(afterBlock5Ptr, block6Output, 1); + idct(block6Output, Y4_block); + block1Output = dataSizeBytes + block6Output; + + if (pPixelBuffer) + { + // TODO: Should probably be using gMasher_pitch_bytes_BB4AF8 ?? + ConvertYuvToRgb888((u32*)pPixelBuffer, xoff, yoff, + this->field_14_video_header.field_4_width, + this->field_14_video_header.field_8_height, + false, + false); + + // pPixelBuffer += gMasher_pitch_bytes_BB4AF8; + } + + yoff += kMacroBlockHeight; + } + xoff += kMacroBlockWidth; + } +} + ALIVE_VAR(1, 0xbbb9b4, s32, gMasher_num_channels_BBB9B4, 0); ALIVE_VAR(1, 0xbbb9a8, s32, gMasher_bits_per_sample_BBB9A8, 0); @@ -1184,6 +1272,45 @@ void Masher::SetElement(s32 x, s32 y, s32 width, s32 height, u16* ptr, u16 value } } +void Masher::SetElement32(s32 x, s32 y, s32 width, s32 height, u32* ptr, u32 value, bool doubleWidth, bool doubleHeight) +{ + if (doubleWidth) + { + x *= 2; + } + + if (doubleHeight) + { + y *= 2; + } + + ptr[(width * y) + x] = value; + + if (doubleWidth) + { + if (x + 1 < width) + { + ptr[(width * y) + x + 1] = value; + } + } + + if (doubleHeight) + { + if (y + 1 < height) + { + ptr[(width * (y + 1)) + x] = value; + + if (doubleWidth) + { + if (x + 1 < width) + { + ptr[(width * (y + 1)) + x + 1] = value; + } + } + } + } +} + uint16_t Masher::rgb888torgb565(Macroblock_RGB_Struct& rgb888Pixel) { uint8_t red = rgb888Pixel.Red; @@ -1197,6 +1324,69 @@ uint16_t Masher::rgb888torgb565(Macroblock_RGB_Struct& rgb888Pixel) return static_cast(r | g | b); } +void Masher::ConvertYuvToRgb888(u32* pixelBuffer, s32 xoff, s32 yoff, s32 width, s32 height, bool doubleWidth, bool doubleHeight) +{ + // convert the Y1 Y2 Y3 Y4 and Cb and Cr blocks into a 16x16 array of (Y, Cb, Cr) pixels + struct Macroblock_YCbCr_Struct final + { + f32 Y; + f32 Cb; + f32 Cr; + }; + + std::array, 16> Macroblock_YCbCr = {}; + + for (s32 x = 0; x < 8; x++) + { + for (s32 y = 0; y < 8; y++) + { + Macroblock_YCbCr[x][y].Y = static_cast(Y1_block[To1d(x, y)]); + Macroblock_YCbCr[x + 8][y].Y = static_cast(Y2_block[To1d(x, y)]); + Macroblock_YCbCr[x][y + 8].Y = static_cast(Y3_block[To1d(x, y)]); + Macroblock_YCbCr[x + 8][y + 8].Y = static_cast(Y4_block[To1d(x, y)]); + + Macroblock_YCbCr[x * 2][y * 2].Cb = static_cast(Cb_block[To1d(x, y)]); + Macroblock_YCbCr[x * 2 + 1][y * 2].Cb = static_cast(Cb_block[To1d(x, y)]); + Macroblock_YCbCr[x * 2][y * 2 + 1].Cb = static_cast(Cb_block[To1d(x, y)]); + Macroblock_YCbCr[x * 2 + 1][y * 2 + 1].Cb = static_cast(Cb_block[To1d(x, y)]); + + Macroblock_YCbCr[x * 2][y * 2].Cr = static_cast(Cr_block[To1d(x, y)]); + Macroblock_YCbCr[x * 2 + 1][y * 2].Cr = static_cast(Cr_block[To1d(x, y)]); + Macroblock_YCbCr[x * 2][y * 2 + 1].Cr = static_cast(Cr_block[To1d(x, y)]); + Macroblock_YCbCr[x * 2 + 1][y * 2 + 1].Cr = static_cast(Cr_block[To1d(x, y)]); + } + } + + // Convert the (Y, Cb, Cr) pixels into RGB pixels + std::array, 16> Macroblock_RGB = {}; + + for (u32 x = 0; x < kMacroBlockWidth; x++) + { + for (u32 y = 0; y < kMacroBlockHeight; y++) + { + const f32 r = (Macroblock_YCbCr[x][y].Y) + 1.402f * Macroblock_YCbCr[x][y].Cb; + const f32 g = (Macroblock_YCbCr[x][y].Y) - 0.3437f * Macroblock_YCbCr[x][y].Cr - 0.7143f * Macroblock_YCbCr[x][y].Cb; + const f32 b = (Macroblock_YCbCr[x][y].Y) + 1.772f * Macroblock_YCbCr[x][y].Cr; + + Macroblock_RGB[x][y].Red = Clamp(r); + Macroblock_RGB[x][y].Green = Clamp(g); + Macroblock_RGB[x][y].Blue = Clamp(b); + + // Due to macro block padding this can be out of bounds + s32 xpos = x + xoff; + s32 ypos = y + yoff; + if (xpos < width && ypos < height) + { + // convert macro block to rgba8888 + u32 pixel32Value = (0xff << 24) | (Macroblock_RGB[x][y].Blue << 16) | (Macroblock_RGB[x][y].Green << 8) | Macroblock_RGB[x][y].Red; + // Actually is no alpha in FMVs + // pixelValue = (pixelValue << 8) + Macroblock_RGB[x][y].A + SetElement32(xpos, ypos, width, height, pixelBuffer, pixel32Value, doubleWidth, doubleHeight); + } + } + } +} + void Masher::ConvertYuvToRgbAndBlit(u16* pixelBuffer, s32 xoff, s32 yoff, s32 width, s32 height, bool doubleWidth, bool doubleHeight) { // convert the Y1 Y2 Y3 Y4 and Cb and Cr blocks into a 16x16 array of (Y, Cb, Cr) pixels diff --git a/Source/AliveLibCommon/Masher.hpp b/Source/AliveLibCommon/Masher.hpp index d3204ac69..4efd67284 100644 --- a/Source/AliveLibCommon/Masher.hpp +++ b/Source/AliveLibCommon/Masher.hpp @@ -93,6 +93,9 @@ class Masher final void Decode_4EA670(); void VideoFrameDecode_4E6C60(u8* pPixelBuffer); + // Same as VideoFrameDecode_4E6C60 but decodes to buffer using raw width/height without blitting. + void VideoFrameDecode_Raw(u8* pPixelBuffer); + // Same as 0x52B015 in MGSI.exe static void CC DDV_Set_Channels_And_BitsPerSample_4ECFD0(s32 numChannels, s32 bitsPerSample); @@ -117,11 +120,12 @@ class Masher final static void SetElement(s32 x, s32 y, s32 width, s32 height, u16* ptr, u16 value, bool doubleWidth, bool doubleHeight); + static void SetElement32(s32 x, s32 y, s32 width, s32 height, u32* ptr, u32 value, bool doubleWidth, bool doubleHeight); static uint16_t rgb888torgb565(Macroblock_RGB_Struct& rgb888Pixel); static void ConvertYuvToRgbAndBlit(u16* pixelBuffer, s32 xoff, s32 yoff, s32 width, s32 height, bool doubleWidth, bool doubleHeight); - + static void ConvertYuvToRgb888(u32* pixelBuffer, s32 xoff, s32 yoff, s32 width, s32 height, bool doubleWidth, bool doubleHeight); void* field_0_file_handle; diff --git a/Source/AliveLibCommon/Sys_common.cpp b/Source/AliveLibCommon/Sys_common.cpp index 9ce0dc20b..1135c3632 100644 --- a/Source/AliveLibCommon/Sys_common.cpp +++ b/Source/AliveLibCommon/Sys_common.cpp @@ -13,6 +13,8 @@ abort(); } +bool gIsGameAE = false; + u32 SYS_GetTicks() { diff --git a/Source/AliveLibCommon/Sys_common.hpp b/Source/AliveLibCommon/Sys_common.hpp index 577f9bc23..f13eca6dd 100644 --- a/Source/AliveLibCommon/Sys_common.hpp +++ b/Source/AliveLibCommon/Sys_common.hpp @@ -78,3 +78,5 @@ inline void Alive_Show_ErrorMsg(const char_type* msg) [[noreturn]] void ALIVE_FATAL(const char_type* errMsg); u32 SYS_GetTicks(); + +extern bool gIsGameAE; \ No newline at end of file diff --git a/Source/Tools/asset_tool/CMakeLists.txt b/Source/Tools/asset_tool/CMakeLists.txt new file mode 100644 index 000000000..4b6794e4d --- /dev/null +++ b/Source/Tools/asset_tool/CMakeLists.txt @@ -0,0 +1,38 @@ +if(UNIX) + SET(BINPATH "bin") +elseif(WIN32) + SET(BINPATH ".") +endif() + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (WIN32) + add_executable(asset_tool WIN32 + asset_tool.cpp + resource.rc + resource.h + asset_common.cpp + asset_common.hpp + asset_meta.cpp + asset_meta.hpp + asset_video.hpp + ) + + if (MSVC) + target_compile_options(asset_tool PRIVATE /W4 /wd4996 /WX /MP) + endif() + + target_include_directories(asset_tool PUBLIC + $ + $ + PRIVATE ${CMAKE_BINARY_DIR}) + target_compile_features(asset_tool + PRIVATE cxx_auto_type + PRIVATE cxx_variadic_templates) + target_compile_definitions(asset_tool PRIVATE "_CRT_SECURE_NO_WARNINGS") + target_link_libraries(asset_tool AliveLibAE AliveLibAO imgui libglew_static jsonxx_reliveapi) + + export(TARGETS asset_tool FILE asset_tool.cmake) + install(TARGETS asset_tool DESTINATION "${BINPATH}") +endif() diff --git a/Source/Tools/asset_tool/app.ico b/Source/Tools/asset_tool/app.ico new file mode 100644 index 000000000..73a39f7bd Binary files /dev/null and b/Source/Tools/asset_tool/app.ico differ diff --git a/Source/Tools/asset_tool/asset_common.cpp b/Source/Tools/asset_tool/asset_common.cpp new file mode 100644 index 000000000..45302b490 --- /dev/null +++ b/Source/Tools/asset_tool/asset_common.cpp @@ -0,0 +1,12 @@ +#include "asset_common.hpp" + +std::string StringToLowerCase(std::string text) +{ + // make copy of string + std::string lowerCase = text; + + // convert to lower case but cast to char + std::transform(lowerCase.begin(), lowerCase.end(), lowerCase.begin(), [](int c) -> char { return static_cast(::tolower(c)); }); + + return lowerCase; +} \ No newline at end of file diff --git a/Source/Tools/asset_tool/asset_common.hpp b/Source/Tools/asset_tool/asset_common.hpp new file mode 100644 index 000000000..084044ca0 --- /dev/null +++ b/Source/Tools/asset_tool/asset_common.hpp @@ -0,0 +1,5 @@ +#include "asset_meta.hpp" + +#include + +std::string StringToLowerCase(std::string text); \ No newline at end of file diff --git a/Source/Tools/asset_tool/asset_meta.cpp b/Source/Tools/asset_tool/asset_meta.cpp new file mode 100644 index 000000000..aa06cd8d6 --- /dev/null +++ b/Source/Tools/asset_tool/asset_meta.cpp @@ -0,0 +1 @@ +#include "asset_meta.hpp" \ No newline at end of file diff --git a/Source/Tools/asset_tool/asset_meta.hpp b/Source/Tools/asset_tool/asset_meta.hpp new file mode 100644 index 000000000..4ca2d9510 --- /dev/null +++ b/Source/Tools/asset_tool/asset_meta.hpp @@ -0,0 +1,104 @@ +#include + +#include +#include + +struct MetaFrameInfo +{ + int original_width; // original width of the frames without padding + int original_height; // original height of the frames without padding and not multiplied by 2 + int x_offset; + int y_offset; +}; + +class AssetMeta +{ +public: + // extra info to make porting from this engine to the new engine easier + std::string game; + int padding; + std::vector metaFrameInfo; + + int size_width; + int size_height; + int offset_x; + int offset_y; + int frame_count; + + bool LoadJSONFromFile(const std::string& path) + { + std::ifstream file; + file.open(path); + + if (!file.good()) + { + return false; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + file.close(); + std::string json = buffer.str(); + + jsonxx::Object metaData; + metaData.parse(json); + + jsonxx::Object metaOffset = metaData.get("offset"); + jsonxx::Object metaSize = metaData.get("size"); + + offset_x = (int)metaOffset.get("x"); + offset_y = (int)metaOffset.get("y"); + + size_width = (int)metaSize.get("w"); + size_height = (int)metaSize.get("h"); + + frame_count = (int)metaData.get("frame_count"); + + return true; + } + + void SaveJSONToFile(const std::string& path, bool writeExtraData = false) + { + jsonxx::Object metaData_offset; + metaData_offset << "x" << offset_x; + metaData_offset << "y" << offset_y; + + jsonxx::Object metaData_originalSize; + metaData_originalSize << "w" << size_width; + metaData_originalSize << "h" << size_height; + + jsonxx::Object metaData; + + metaData << "offset" << metaData_offset; + metaData << "size" << metaData_originalSize; + + metaData << "frame_count" << frame_count; + + if (writeExtraData) + { + jsonxx::Object extra; + jsonxx::Array frameInfo; + + extra << "game" << game; + extra << "padding" << padding; + + for (int i = 0; i < frame_count; i++) + { + jsonxx::Object info; + info << "original_width" << metaFrameInfo.at(i).original_width; + info << "original_height" << metaFrameInfo.at(i).original_height; + info << "x_offset" << metaFrameInfo.at(i).x_offset; + info << "y_offset" << metaFrameInfo.at(i).y_offset; + frameInfo.append(info); + } + + extra << "frames_info" << frameInfo; + metaData << "extra" << extra; + } + + std::ofstream file; + file.open(path); + file << metaData.json(); + file.close(); + } +}; \ No newline at end of file diff --git a/Source/Tools/asset_tool/asset_tool.cpp b/Source/Tools/asset_tool/asset_tool.cpp new file mode 100644 index 000000000..746a53ff0 --- /dev/null +++ b/Source/Tools/asset_tool/asset_tool.cpp @@ -0,0 +1,2027 @@ +#include "relive_config.h" +#include "logger.hpp" +#include "FunctionFwd.hpp" +#include "SDL_main.h" +#include "Io.hpp" +#include "Sys.hpp" +#include "imgui.h" +#include "imgui_impl_sdl.h" +#include "misc/cpp/imgui_stdlib.h" +#include "imgui_impl_opengl3.h" +#include "GL/glew.h" +#include "../AliveLibAE/stdlib.hpp" + +#define MAGIC_ENUM_RANGE_MIN 0 +#define MAGIC_ENUM_RANGE_MAX 1024 +#include "magic_enum/include/magic_enum.hpp" + +#include "../AliveLibAE/LvlArchive.hpp" +#include "../AliveLibAE/Game.hpp" +#include "../AliveLibAE/PathData.hpp" +#include "../AliveLibAE/ResourceManager.hpp" +#include "../AliveLibAE/Animation.hpp" +#include "../AliveLibAE/Compression.hpp" +#include "../AliveLibAE/PsxDisplay.hpp" + +#include "../AliveLibAO/Compression.hpp" + +#include "AnimResources.hpp" +#include "../AliveLibAE/ObjectIds.hpp" + +#include +#include +#include +#include + +#include "stb/stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb/stb_image_write.h" + +#include "asset_common.hpp" +#include "asset_video.hpp" + +namespace fs = std::filesystem; + +#pragma warning(disable:4312) + +bool CC RunningAsInjectedDll() +{ + return false; +} + +struct RGBAPixel final { + unsigned char R; + unsigned char G; + unsigned char B; + unsigned char A; +}; + +struct PreviewTexture final +{ + GLuint id; + int width; + int height; + int offsetX; + int offsetY; +}; + +struct PreviewExternalTexture final +{ + GLuint id; +}; + +struct LoadedAnimRecord final +{ + std::string name; + AnimRecord record; +}; + +const ImGuiWindowFlags kImGuiFullscreenFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; + +struct Bounds final +{ + int x; + int y; + int w; + int h; +}; + +struct LevelInfo +{ + const char* lvl_file; + const char* lvl_name; +}; + +const LevelInfo AeLvls[] = { + {"st.lvl", "Start"}, + {"mi.lvl", "Mines"}, + {"ne.lvl", "Necrum"}, + {"ba.lvl", ""}, + {"bm.lvl", ""}, + {"br.lvl", ""}, + {"bw.lvl", "Bonewerkz"}, + {"fd.lvl", "Feeco Depot"}, + {"pv.lvl", "Paramonia Vault"}, + {"sv.lvl", "Scrabania Vault"}, + {"cr.lvl", "Credits"}, +}; + +const LevelInfo AoLvls[]{ + {"c1.lvl", ""}, + {"d1.lvl", ""}, + {"d2.lvl", ""}, + {"d7.lvl", ""}, + {"e1.lvl", ""}, + {"e2.lvl", ""}, + {"f1.lvl", ""}, + {"f2.lvl", ""}, + {"f4.lvl", ""}, + {"l1.lvl", ""}, + {"r1.lvl", ""}, + {"r2.lvl", ""}, + {"r6.lvl", ""}, + {"s1.lvl", ""}, +}; + +struct AppTool +{ + const char* name; + std::function update_func; +}; + +struct FrameTableEntry +{ + std::string file_prefix; + int x_offset; + int y_offset; + bool file_exists; +}; + +// Big old table to convert ae file names to new names +static std::map frametableMatches = { + {7812, {"ABEBLOW.BAN_0", 0, 0, false } }, + {7732, {"ABEBLOW.BAN_1", 0, 0, false } }, + {7772, {"ABEBLOW.BAN_2", 0, 0, false } }, + {6520, {"ABEBOMB.BAN_0", 0, 0, false } }, + {271248, {"ABEBSIC.BAN_0", 0, 0, false } }, + {269688, {"ABEBSIC.BAN_1", 0, 0, false } }, + {269708, {"ABEBSIC.BAN_2", 0, 0, false } }, + {269764, {"ABEBSIC.BAN_3", 0, 0, false } }, + {269784, {"ABEBSIC.BAN_4", 0, 0, false } }, + {269804, {"ABEBSIC.BAN_5", 0, 0, false } }, + {269856, {"ABEBSIC.BAN_6", 0, 0, false } }, + {269876, {"ABEBSIC.BAN_7", 0, 0, false } }, + {269928, {"ABEBSIC.BAN_8", 0, 0, false } }, + {269976, {"ABEBSIC.BAN_9", 0, 0, false } }, + {270024, {"ABEBSIC.BAN_10", 0, 0, false } }, + {270060, {"ABEBSIC.BAN_11", 0, 0, false } }, + {270092, {"ABEBSIC.BAN_12", 0, 0, false } }, + {270120, {"ABEBSIC.BAN_13", 0, 0, false } }, + {270140, {"ABEBSIC.BAN_14", 0, 0, false } }, + {270160, {"ABEBSIC.BAN_15", 0, 0, false } }, + {270180, {"ABEBSIC.BAN_16", 0, 0, false } }, + {270240, {"ABEBSIC.BAN_17", 0, 0, false } }, + {270252, {"ABEBSIC.BAN_18", 0, 0, false } }, + {270268, {"ABEBSIC.BAN_19", 0, 0, false } }, + {270288, {"ABEBSIC.BAN_20", 0, 0, false } }, + {270308, {"ABEBSIC.BAN_21", 0, 0, false } }, + {270328, {"ABEBSIC.BAN_22", 0, 0, false } }, + {270408, {"ABEBSIC.BAN_23", 0, 0, false } }, + {270428, {"ABEBSIC.BAN_24", 0, 0, false } }, + {270468, {"ABEBSIC.BAN_25", 0, 0, false } }, + {270488, {"ABEBSIC.BAN_26", 0, 0, false } }, + {270508, {"ABEBSIC.BAN_27", 0, 0, false } }, + {270596, {"ABEBSIC.BAN_28", 0, 0, false } }, + {270616, {"ABEBSIC.BAN_29", 0, 0, false } }, + {270668, {"ABEBSIC.BAN_30", 0, 0, false } }, + {270688, {"ABEBSIC.BAN_31", 0, 0, false } }, + {270708, {"ABEBSIC.BAN_32", 0, 0, false } }, + {270728, {"ABEBSIC.BAN_33", 0, 0, false } }, + {270748, {"ABEBSIC.BAN_34", 0, 0, false } }, + {270820, {"ABEBSIC.BAN_35", 0, 0, false } }, + {270840, {"ABEBSIC.BAN_36", 0, 0, false } }, + {270860, {"ABEBSIC.BAN_37", 0, 0, false } }, + {270932, {"ABEBSIC.BAN_38", 0, 0, false } }, + {270944, {"ABEBSIC.BAN_39", 0, 0, false } }, + {270964, {"ABEBSIC.BAN_40", 0, 0, false } }, + {271004, {"ABEBSIC.BAN_41", 0, 0, false } }, + {271056, {"ABEBSIC.BAN_42", 0, 0, false } }, + {271080, {"ABEBSIC.BAN_43", 0, 0, false } }, + {271120, {"ABEBSIC.BAN_44", 0, 0, false } }, + {271152, {"ABEBSIC.BAN_45", 0, 0, false } }, + {271192, {"ABEBSIC.BAN_46", 0, 0, false } }, + {59112, {"ABEBSIC1.BAN_0", 0, 0, false } }, + {58748, {"ABEBSIC1.BAN_1", 0, 0, false } }, + {58768, {"ABEBSIC1.BAN_2", 0, 0, false } }, + {58788, {"ABEBSIC1.BAN_3", 0, 0, false } }, + {58808, {"ABEBSIC1.BAN_4", 0, 0, false } }, + {58888, {"ABEBSIC1.BAN_5", 0, 0, false } }, + {58920, {"ABEBSIC1.BAN_6", 0, 0, false } }, + {58956, {"ABEBSIC1.BAN_7", 0, 0, false } }, + {58992, {"ABEBSIC1.BAN_8", 0, 0, false } }, + {59028, {"ABEBSIC1.BAN_9", 0, 0, false } }, + {59064, {"ABEBSIC1.BAN_10", 0, 0, false } }, + {19184, {"ABEDOOR.BAN_0", 0, 0, false } }, + {19040, {"ABEDOOR.BAN_1", 0, 0, false } }, + {19088, {"ABEDOOR.BAN_2", 0, 0, false } }, + {19136, {"ABEDOOR.BAN_3", 0, 0, false } }, + {32168, {"ABEEDGE.BAN_0", 0, 0, false } }, + {32012, {"ABEEDGE.BAN_1", 0, 0, false } }, + {32040, {"ABEEDGE.BAN_2", 0, 0, false } }, + {32068, {"ABEEDGE.BAN_3", 0, 0, false } }, + {32112, {"ABEEDGE.BAN_4", 0, 0, false } }, + {32132, {"ABEEDGE.BAN_5", 0, 0, false } }, + {32152, {"ABEEDGE.BAN_6", 0, 0, false } }, + {28824, {"ABEGAS.BAN_0", 0, 0, false } }, + {57712, {"ABEHOIST.BAN_0", 0, 0, false } }, + {57440, {"ABEHOIST.BAN_1", 0, 0, false } }, + {57540, {"ABEHOIST.BAN_2", 0, 0, false } }, + {57580, {"ABEHOIST.BAN_3", 0, 0, false } }, + {57680, {"ABEHOIST.BAN_4", 0, 0, false } }, + {49912, {"ABEKNBK.BAN_0", 0, 0, false } }, + {49740, {"ABEKNBK.BAN_1", 0, 0, false } }, + {49800, {"ABEKNBK.BAN_2", 0, 0, false } }, + {49856, {"ABEKNBK.BAN_3", 0, 0, false } }, + {17240, {"ABEKNFD.BAN_0", 0, 0, false } }, + {17180, {"ABEKNFD.BAN_1", 0, 0, false } }, + {24628, {"ABEKNOKZ.BAN_0", 0, 0, false } }, + {24580, {"ABEKNOKZ.BAN_1", 0, 0, false } }, + {22596, {"ABELIFT.BAN_0", 0, 0, false } }, + {22464, {"ABELIFT.BAN_1", 0, 0, false } }, + {22496, {"ABELIFT.BAN_2", 0, 0, false } }, + {22548, {"ABELIFT.BAN_3", 0, 0, false } }, + {22572, {"ABELIFT.BAN_4", 0, 0, false } }, + {10040, {"ABEOMM.BAN_0", 0, 0, false } }, + {9992, {"ABEOMM.BAN_1", 0, 0, false } }, + {6004, {"ABEPICK.BAN_0", 0, 0, false } }, + {11396, {"ABEPULL.BAN_0", 0, 0, false } }, + {16220, {"ABESTONE.BAN_0", 0, 0, false } }, + {16040, {"ABESTONE.BAN_1", 0, 0, false } }, + {16096, {"ABESTONE.BAN_2", 0, 0, false } }, + {31656, {"ABETHROW.BAN_0", 0, 0, false } }, + {31392, {"ABETHROW.BAN_1", 0, 0, false } }, + {31432, {"ABETHROW.BAN_2", 0, 0, false } }, + {31504, {"ABETHROW.BAN_3", 0, 0, false } }, + {31576, {"ABETHROW.BAN_4", 0, 0, false } }, + {31632, {"ABETHROW.BAN_5", 0, 0, false } }, + {20572, {"ABEWELL.BAN_0", 0, 0, false } }, + {20404, {"ABEWELL.BAN_1", 0, 0, false } }, + {20484, {"ABEWELL.BAN_2", 0, 0, false } }, + {20552, {"ABEWELL.BAN_3", 0, 0, false } }, + {8588, {"ABECAR.BAN_0", 0, 0, false } }, + {8540, {"ABECAR.BAN_1", 0, 0, false } }, + {8772, {"ABEMORPH.BAN_0", 0, 0, false } }, + {8732, {"ABEMORPH.BAN_1", 0, 0, false } }, + {5748, {"ABENOELM.BND_0", 0, 0, false } }, + {5724, {"ABENOELM.BND_1", 0, 0, false } }, + {8104, {"ABENOELM.BND_0", 0, 0, false } }, + {11888, {"ABEWORK.BAN_0", 0, 0, false } }, + {11816, {"ABEWORK.BAN_1", 0, 0, false } }, + {11856, {"ABEWORK.BAN_2", 0, 0, false } }, + {4488, {"DUST.BAN_0", 0, 0, false } }, + {216, {"BLOOD.BAN_0", 0, 0, false } }, + {192, {"BLOOD.BAN_1", 0, 0, false } }, + {204, {"BLOOD.BAN_2", 0, 0, false } }, + {33552, {"SLGSLEEP.BAN_0", 0, 0, false } }, + {33448, {"SLGSLEEP.BAN_1", 0, 0, false } }, + {6560, {"SLGBLOW.BAN_0", 0, 0, false } }, + {6480, {"SLGBLOW.BAN_1", 0, 0, false } }, + {6520, {"SLGBLOW.BAN_2", 0, 0, false } }, + {12660, {"SLGBEAT.BAN_0", 0, 0, false } }, + {30628, {"SLGEDGE.BAN_0", 0, 0, false } }, + {30560, {"SLGEDGE.BAN_1", 0, 0, false } }, + {30592, {"SLGEDGE.BAN_2", 0, 0, false } }, + {13016, {"SLGKNFD.BAN_0", 0, 0, false } }, + {30628, {"SLGEDGE.BAN_0", 0, 0, false } }, + {30560, {"SLGEDGE.BAN_1", 0, 0, false } }, + {30592, {"SLGEDGE.BAN_2", 0, 0, false } }, + {9204, {"SLGSMASH.BAN_0", 0, 0, false } }, + {12612, {"SLGLEVER.BAN_0", 0, 0, false } }, + {23200, {"SLGLIFT.BAN_0", 0, 0, false } }, + {23048, {"SLGLIFT.BAN_1", 0, 0, false } }, + {23072, {"SLGLIFT.BAN_2", 0, 0, false } }, + {23096, {"SLGLIFT.BAN_3", 0, 0, false } }, + {23148, {"SLGLIFT.BAN_4", 0, 0, false } }, + {136132, {"SLIG.BND_0", 0, 0, false } }, + {135360, {"SLIG.BND_1", 0, 0, false } }, + {135440, {"SLIG.BND_2", 0, 0, false } }, + {135512, {"SLIG.BND_3", 0, 0, false } }, + {135544, {"SLIG.BND_4", 0, 0, false } }, + {135576, {"SLIG.BND_5", 0, 0, false } }, + {135632, {"SLIG.BND_6", 0, 0, false } }, + {135652, {"SLIG.BND_7", 0, 0, false } }, + {135696, {"SLIG.BND_8", 0, 0, false } }, + {135768, {"SLIG.BND_9", 0, 0, false } }, + {135788, {"SLIG.BND_10", 0, 0, false } }, + {135804, {"SLIG.BND_11", 0, 0, false } }, + {135824, {"SLIG.BND_12", 0, 0, false } }, + {135844, {"SLIG.BND_13", 0, 0, false } }, + {135876, {"SLIG.BND_14", 0, 0, false } }, + {135896, {"SLIG.BND_15", 0, 0, false } }, + {135916, {"SLIG.BND_16", 0, 0, false } }, + {135936, {"SLIG.BND_17", 0, 0, false } }, + {135976, {"SLIG.BND_18", 0, 0, false } }, + {136012, {"SLIG.BND_19", 0, 0, false } }, + {136048, {"SLIG.BND_20", 0, 0, false } }, + {136088, {"SLIG.BND_21", 0, 0, false } }, + {33348, {"SLIG.BND_0", 0, 0, false } }, + {33184, {"SLIG.BND_1", 0, 0, false } }, + {33228, {"SLIG.BND_2", 0, 0, false } }, + {960, {"SLIG.BND_0", 0, 0, false } }, + {320, {"SLIG.BND_0", 0, 0, false } }, + {24628, {"SLIGZ.BND_0", 0, 0, false } }, + {24580, {"SLIGZ.BND_1", 0, 0, false } }, + {2832, {"SLIGZ.BND_0", 0, 0, false } }, + {9260, {"SLIGZ.BND_0", 0, 0, false } }, + {9208, {"SLIGZ.BND_1", 0, 0, false } }, + {216, {"BLOOD.BAN_0", 0, 0, false } }, + {192, {"BLOOD.BAN_1", 0, 0, false } }, + {204, {"BLOOD.BAN_2", 0, 0, false } }, + {6560, {"SLGBLOW.BAN_0", 0, 0, false } }, + {6480, {"SLGBLOW.BAN_1", 0, 0, false } }, + {6520, {"SLGBLOW.BAN_2", 0, 0, false } }, + {1252, {"GRENADE.BAN_0", 0, 0, false } }, + {14108, {"SMEXP.BAN_0", 0, 0, false } }, + {2244, {"METAL.BAN_0", 0, 0, false } }, + {7812, {"ABEBLOW.BAN_0", 0, 0, false } }, + {7732, {"ABEBLOW.BAN_1", 0, 0, false } }, + {7772, {"ABEBLOW.BAN_2", 0, 0, false } }, + {960, {"BIGFLASH.BAN_0", 0, 0, false } }, + {5264, {"VAPOR.BAN_0", 0, 0, false } }, + {7544, {"DOGBLOW.BAN_0", 0, 0, false } }, + {7504, {"DOGBLOW.BAN_1", 0, 0, false } }, + {117752, {"FLYSLIG.BND_0", 0, 0, false } }, + {116888, {"FLYSLIG.BND_1", 0, 0, false } }, + {116912, {"FLYSLIG.BND_2", 0, 0, false } }, + {116936, {"FLYSLIG.BND_3", 0, 0, false } }, + {116988, {"FLYSLIG.BND_4", 0, 0, false } }, + {117012, {"FLYSLIG.BND_5", 0, 0, false } }, + {117036, {"FLYSLIG.BND_6", 0, 0, false } }, + {117060, {"FLYSLIG.BND_7", 0, 0, false } }, + {117084, {"FLYSLIG.BND_8", 0, 0, false } }, + {117132, {"FLYSLIG.BND_9", 0, 0, false } }, + {117188, {"FLYSLIG.BND_10", 0, 0, false } }, + {117276, {"FLYSLIG.BND_11", 0, 0, false } }, + {117296, {"FLYSLIG.BND_12", 0, 0, false } }, + {117316, {"FLYSLIG.BND_13", 0, 0, false } }, + {117336, {"FLYSLIG.BND_14", 0, 0, false } }, + {117356, {"FLYSLIG.BND_15", 0, 0, false } }, + {117376, {"FLYSLIG.BND_16", 0, 0, false } }, + {117396, {"FLYSLIG.BND_17", 0, 0, false } }, + {117424, {"FLYSLIG.BND_18", 0, 0, false } }, + {117444, {"FLYSLIG.BND_19", 0, 0, false } }, + {117464, {"FLYSLIG.BND_20", 0, 0, false } }, + {117492, {"FLYSLIG.BND_21", 0, 0, false } }, + {117524, {"FLYSLIG.BND_22", 0, 0, false } }, + {117552, {"FLYSLIG.BND_23", 0, 0, false } }, + {117584, {"FLYSLIG.BND_24", 0, 0, false } }, + {117616, {"FLYSLIG.BND_25", 0, 0, false } }, + {117648, {"FLYSLIG.BND_26", 0, 0, false } }, + {117668, {"FLYSLIG.BND_27", 0, 0, false } }, + {117700, {"FLYSLIG.BND_28", 0, 0, false } }, + {117736, {"FLYSLIG.BND_29", 0, 0, false } }, + {216, {"BLOOD.BAN_0", 0, 0, false } }, + {192, {"BLOOD.BAN_1", 0, 0, false } }, + {204, {"BLOOD.BAN_2", 0, 0, false } }, + {6560, {"SLGBLOW.BAN_0", 0, 0, false } }, + {6480, {"SLGBLOW.BAN_1", 0, 0, false } }, + {6520, {"SLGBLOW.BAN_2", 0, 0, false } }, + {1252, {"GRENADE.BAN_0", 0, 0, false } }, + {14108, {"SMEXP.BAN_0", 0, 0, false } }, + {2244, {"METAL.BAN_0", 0, 0, false } }, + {7812, {"ABEBLOW.BAN_0", 0, 0, false } }, + {7732, {"ABEBLOW.BAN_1", 0, 0, false } }, + {7772, {"ABEBLOW.BAN_2", 0, 0, false } }, + {960, {"BIGFLASH.BAN_0", 0, 0, false } }, + {5264, {"VAPOR.BAN_0", 0, 0, false } }, + {7544, {"DOGBLOW.BAN_0", 0, 0, false } }, + {7504, {"DOGBLOW.BAN_1", 0, 0, false } }, + {4364, {"LOCKER.BAN_0", 0, 0, false } }, + {4352, {"LOCKER.BAN_1", 0, 0, false } }, + {420, {"CSLGBUTN.BAN_0", 0, 0, false } }, + {408, {"CSLGBUTN.BAN_1", 0, 0, false } }, + {54188, {"CRAWLSLG.BND_0", 0, 0, false } }, + {53560, {"CRAWLSLG.BND_1", 0, 0, false } }, + {53616, {"CRAWLSLG.BND_2", 0, 0, false } }, + {53684, {"CRAWLSLG.BND_3", 0, 0, false } }, + {53720, {"CRAWLSLG.BND_4", 0, 0, false } }, + {53736, {"CRAWLSLG.BND_5", 0, 0, false } }, + {53752, {"CRAWLSLG.BND_6", 0, 0, false } }, + {53780, {"CRAWLSLG.BND_7", 0, 0, false } }, + {53816, {"CRAWLSLG.BND_8", 0, 0, false } }, + {53852, {"CRAWLSLG.BND_9", 0, 0, false } }, + {53928, {"CRAWLSLG.BND_10", 0, 0, false } }, + {53964, {"CRAWLSLG.BND_11", 0, 0, false } }, + {54000, {"CRAWLSLG.BND_12", 0, 0, false } }, + {54032, {"CRAWLSLG.BND_13", 0, 0, false } }, + {54076, {"CRAWLSLG.BND_14", 0, 0, false } }, + {54096, {"CRAWLSLG.BND_15", 0, 0, false } }, + {54120, {"CRAWLSLG.BND_16", 0, 0, false } }, + {54140, {"CRAWLSLG.BND_17", 0, 0, false } }, + {54160, {"CRAWLSLG.BND_18", 0, 0, false } }, + {51588, {"BGEXPLD.BAN_0", 0, 0, false } }, + {960, {"BIGFLASH.BAN_0", 0, 0, false } }, + {216, {"BLOOD.BAN_0", 0, 0, false } }, + {192, {"BLOOD.BAN_1", 0, 0, false } }, + {204, {"BLOOD.BAN_2", 0, 0, false } }, + {308, {"BLOODROP.BAN_0", 0, 0, false } }, + {556, {"BOMBFLSH.BAN_0", 0, 0, false } }, + {544, {"BOMBFLSH.BAN_1", 0, 0, false } }, + {8788, {"BONEBAG.BAN_0", 0, 0, false } }, + {8708, {"BONEBAG.BAN_1", 0, 0, false } }, + {8748, {"BONEBAG.BAN_2", 0, 0, false } }, + {17548, {"MOVEBOMB.BAN_0", 0, 0, false } }, + {15888, {"MEATSACK.BAN_0", 0, 0, false } }, + {15848, {"MEATSACK.BAN_1", 0, 0, false } }, + {10008, {"DEADFLR.BAN_0", 0, 0, false } }, + {9912, {"DEADFLR.BAN_1", 0, 0, false } }, + {9940, {"DEADFLR.BAN_2", 0, 0, false } }, + {6484, {"DEBRIS00.BAN_0", 0, 0, false } }, + {8704, {"DOOR.BAN_0", 0, 0, false } }, + {8692, {"DOOR.BAN_1", 0, 0, false } }, + {5836, {"DOVBASIC.BAN_0", 0, 0, false } }, + {5516, {"DOVBASIC.BAN_1", 0, 0, false } }, + {5580, {"DOVBASIC.BAN_2", 0, 0, false } }, + {5760, {"DOVBASIC.BAN_3", 0, 0, false } }, + {204, {"DRPROCK.BAN_0", 0, 0, false } }, + {180, {"DRPROCK.BAN_1", 0, 0, false } }, + {192, {"DRPROCK.BAN_2", 0, 0, false } }, + {288, {"DRPSPRK.BAN_0", 0, 0, false } }, + {252, {"DRPSPRK.BAN_1", 0, 0, false } }, + {272, {"DRPSPRK.BAN_2", 0, 0, false } }, + {15384, {"ELECWALL.BAN_0", 0, 0, false } }, + {1252, {"EMOHAP.BAN_0", 0, 0, false } }, + {1076, {"EMOANGRY.BAN_0", 0, 0, false } }, + {1248, {"EMONORM.BAN_0", 0, 0, false } }, + {51156, {"EXPLO2.BAN_0", 0, 0, false } }, + {14108, {"SMEXP.BAN_0", 0, 0, false } }, + {2228, {"MAIMORB.BAN_0", 0, 0, false } }, + {316, {"BREWBTN.BAN_0", 0, 0, false } }, + {3532, {"EVILFART.BAN_0", 0, 0, false } }, + {4208, {"FARTFAN.BAN_0", 0, 0, false } }, + {4256, {"FALLROCK.BAN_0", 0, 0, false } }, + {4232, {"FALLROCK.BAN_1", 0, 0, false } }, + {5156, {"FIRE.BAN_0", 0, 0, false } }, + {7068, {"GHOSTTRP.BAN_0", 0, 0, false } }, + {6976, {"GHOSTTRP.BAN_1", 0, 0, false } }, + {7056, {"GHOSTTRP.BAN_2", 0, 0, false } }, + {1400, {"GLOW1.BAN_0", 0, 0, false } }, + {50236, {"GREETER.BAN_0", 0, 0, false } }, + {50028, {"GREETER.BAN_1", 0, 0, false } }, + {50072, {"GREETER.BAN_2", 0, 0, false } }, + {50104, {"GREETER.BAN_3", 0, 0, false } }, + {50144, {"GREETER.BAN_4", 0, 0, false } }, + {50172, {"GREETER.BAN_5", 0, 0, false } }, + {50196, {"GREETER.BAN_6", 0, 0, false } }, + {50212, {"GREETER.BAN_7", 0, 0, false } }, + {356, {"GOLDGLOW.BAN_0", 0, 0, false } }, + {112, {"GRENGLOW.BAN_0", 0, 0, false } }, + {13936, {"HIGHLITE.BAN_0", 0, 0, false } }, + {13900, {"HIGHLITE.BAN_1", 0, 0, false } }, + {13912, {"HIGHLITE.BAN_2", 0, 0, false } }, + {13924, {"HIGHLITE.BAN_3", 0, 0, false } }, + {256, {"HUBLIGHT.BAN_0", 0, 0, false } }, + {244, {"HUBLIGHT.BAN_1", 0, 0, false } }, + {256, {"HUBRED.BAN_0", 0, 0, false } }, + {244, {"HUBRED.BAN_1", 0, 0, false } }, + {784, {"LANDMINE.BAN_0", 0, 0, false } }, + {900, {"LOADING.BAN_0", 0, 0, false } }, + {2244, {"METAL.BAN_0", 0, 0, false } }, + {1736, {"MFLARE.BAN_0", 0, 0, false } }, + {400, {"MINEFLSH.BAN_0", 0, 0, false } }, + {23280, {"MOTION.BAN_0", 0, 0, false } }, + {5328, {"MUDBTLNK.BAN_0", 0, 0, false } }, + {5320, {"MUDCHSL.BAN_0", 0, 0, false } }, + {5276, {"MUDCHSL.BAN_1", 0, 0, false } }, + {5308, {"MUDCHSL.BAN_2", 0, 0, false } }, + {9640, {"MUDIDLE.BAN_0", 0, 0, false } }, + {11392, {"MUDLTUS.BAN_0", 0, 0, false } }, + {11300, {"MUDLTUS.BAN_1", 0, 0, false } }, + {11336, {"MUDLTUS.BAN_2", 0, 0, false } }, + {5280, {"MUDODUCK.BAN_0", 0, 0, false } }, + {5236, {"MUDODUCK.BAN_1", 0, 0, false } }, + {5256, {"MUDODUCK.BAN_2", 0, 0, false } }, + {9388, {"MUDSCRUB.BAN_0", 0, 0, false } }, + {21000, {"MUDTORT.BAN_0", 0, 0, false } }, + {20864, {"MUDTORT.BAN_1", 0, 0, false } }, + {20892, {"MUDTORT.BAN_2", 0, 0, false } }, + {1672, {"OMMFLARE.BAN_0", 0, 0, false } }, + {1632, {"OMMFLARE.BAN_1", 0, 0, false } }, + {808, {"SPARKS.BAN_0", 0, 0, false } }, + {3092, {"PULLRING.BAN_0", 0, 0, false } }, + {3020, {"PULLRING.BAN_1", 0, 0, false } }, + {3060, {"PULLRING.BAN_2", 0, 0, false } }, + {240, {"SHADOW.BAN_0", 0, 0, false } }, + {320, {"SHELL.BAN_0", 0, 0, false } }, + {2808, {"SLURG.BAN_0", 0, 0, false } }, + {2708, {"SLURG.BAN_1", 0, 0, false } }, + {2740, {"SLURG.BAN_2", 0, 0, false } }, + {2772, {"SLURG.BAN_3", 0, 0, false } }, + {288, {"SPLINE.BAN_0", 0, 0, false } }, + {276, {"SPLINE.BAN_1", 0, 0, false } }, + {1536, {"SPOTLITE.BAN_0", 0, 0, false } }, + {5084, {"SQBSMK.BAN_0", 0, 0, false } }, + {3228, {"STATICON.BAN_0", 0, 0, false } }, + {3204, {"STATICON.BAN_1", 0, 0, false } }, + {3216, {"STATICON.BAN_2", 0, 0, false } }, + {252, {"STATUSLT.BAN_0", 0, 0, false } }, + {240, {"STATUSLT.BAN_1", 0, 0, false } }, + {1704, {"STICK.BAN_0", 0, 0, false } }, + {5848, {"SWITCH1.BAN_0", 0, 0, false } }, + {5696, {"SWITCH1.BAN_1", 0, 0, false } }, + {5708, {"SWITCH1.BAN_2", 0, 0, false } }, + {5760, {"SWITCH1.BAN_3", 0, 0, false } }, + {5796, {"SWITCH1.BAN_4", 0, 0, false } }, + {8192, {"TBOMB.BAN_0", 0, 0, false } }, + {8048, {"TBOMB.BAN_1", 0, 0, false } }, + {8120, {"TBOMB.BAN_2", 0, 0, false } }, + {500, {"TEARS.BAN_0", 0, 0, false } }, + {588, {"TRIGGER.BAN_0", 0, 0, false } }, + {568, {"TRIGGER.BAN_1", 0, 0, false } }, + {104, {"WDROP.BAN_0", 0, 0, false } }, + {332, {"SPLASH.BAN_0", 0, 0, false } }, + {476, {"WELLLEAF.BAN_0", 0, 0, false } }, + {2728, {"WORKWHEL.BAN_0", 0, 0, false } }, + {2716, {"WORKWHEL.BAN_1", 0, 0, false } }, + {6004, {"BTHROW.BND_0", 0, 0, false } }, + {31656, {"BTHROW.BND_0", 0, 0, false } }, + {31392, {"BTHROW.BND_1", 0, 0, false } }, + {31432, {"BTHROW.BND_2", 0, 0, false } }, + {31504, {"BTHROW.BND_3", 0, 0, false } }, + {31576, {"BTHROW.BND_4", 0, 0, false } }, + {31632, {"BTHROW.BND_5", 0, 0, false } }, + {456, {"BTHROW.BND_0", 0, 0, false } }, + {848, {"BOMB.BND_0", 0, 0, false } }, + {836, {"BOMB.BND_1", 0, 0, false } }, + {556, {"BOMB.BND_0", 0, 0, false } }, + {544, {"BOMB.BND_1", 0, 0, false } }, + {784, {"MINE.BND_0", 0, 0, false } }, + {400, {"MINE.BND_0", 0, 0, false } }, + {8192, {"UXB.BND_0", 0, 0, false } }, + {8048, {"UXB.BND_1", 0, 0, false } }, + {8120, {"UXB.BND_2", 0, 0, false } }, + {556, {"UXB.BND_0", 0, 0, false } }, + {544, {"UXB.BND_1", 0, 0, false } }, + {6520, {"EXPLODE.BND_0", 0, 0, false } }, + {6484, {"EXPLODE.BND_0", 0, 0, false } }, + {51588, {"EXPLODE.BND_0", 0, 0, false } }, + {5280, {"MUDWORK.BND_0", 0, 0, false } }, + {5236, {"MUDWORK.BND_1", 0, 0, false } }, + {5256, {"MUDWORK.BND_2", 0, 0, false } }, + {5328, {"MUDWORK.BND_0", 0, 0, false } }, + {4168, {"PORTAL.BND_0", 0, 0, false } }, + {4068, {"PORTAL.BND_1", 0, 0, false } }, + {4144, {"PORTAL.BND_2", 0, 0, false } }, + {4256, {"PORTAL.BND_0", 0, 0, false } }, + {13576, {"PORTAL.BND_0", 0, 0, false } }, + {8772, {"SHRYPORT.BND_0", 0, 0, false } }, + {8732, {"SHRYPORT.BND_1", 0, 0, false } }, + {82824, {"SHRYPORT.BND_0", 0, 0, false } }, + {82676, {"SHRYPORT.BND_1", 0, 0, false } }, + {82712, {"SHRYPORT.BND_2", 0, 0, false } }, + {15632, {"MIP01C20.CAM_0", 0, 0, false } }, + {15632, {"MIP01C24.CAM_0", 0, 0, false } }, + {15632, {"MIP01C25.CAM_0", 0, 0, false } }, + {15632, {"MIP02C04.CAM_0", 0, 0, false } }, + {15632, {"MIP03C01.CAM_0", 0, 0, false } }, + {15632, {"MIP03C03.CAM_0", 0, 0, false } }, + {15632, {"MIP03C05.CAM_0", 0, 0, false } }, + {29772, {"MIP04C01.CAM_0", 0, 0, false } }, + {29700, {"MIP04C01.CAM_1", 0, 0, false } }, + {29748, {"MIP04C01.CAM_2", 0, 0, false } }, + {7104, {"MIP04C18.CAM_0", 0, 0, false } }, + {29772, {"MIP04C29.CAM_0", 0, 0, false } }, + {29700, {"MIP04C29.CAM_1", 0, 0, false } }, + {29748, {"MIP04C29.CAM_2", 0, 0, false } }, + {15632, {"MIP04C32.CAM_0", 0, 0, false } }, + {29772, {"MIP04C33.CAM_0", 0, 0, false } }, + {29700, {"MIP04C33.CAM_1", 0, 0, false } }, + {29748, {"MIP04C33.CAM_2", 0, 0, false } }, + {29772, {"MIP04C36.CAM_0", 0, 0, false } }, + {29700, {"MIP04C36.CAM_1", 0, 0, false } }, + {29748, {"MIP04C36.CAM_2", 0, 0, false } }, + {7104, {"MIP04C36.CAM_0", 0, 0, false } }, + {1736, {"MIP05C01.CAM_0", 0, 0, false } }, + {23280, {"MIP05C01.CAM_0", 0, 0, false } }, + {1736, {"MIP05C02.CAM_0", 0, 0, false } }, + {23280, {"MIP05C02.CAM_0", 0, 0, false } }, + {1736, {"MIP05C03.CAM_0", 0, 0, false } }, + {23280, {"MIP05C03.CAM_0", 0, 0, false } }, + {1736, {"MIP05C04.CAM_0", 0, 0, false } }, + {23280, {"MIP05C04.CAM_0", 0, 0, false } }, + {1736, {"MIP05C09.CAM_0", 0, 0, false } }, + {23280, {"MIP05C09.CAM_0", 0, 0, false } }, + {1736, {"MIP05C10.CAM_0", 0, 0, false } }, + {23280, {"MIP05C10.CAM_0", 0, 0, false } }, + {964, {"MIP08C06.CAM_0", 0, 0, false } }, + {15632, {"MIP08C20.CAM_0", 0, 0, false } }, + {964, {"MIP08C21.CAM_0", 0, 0, false } }, + {964, {"MIP08C32.CAM_0", 0, 0, false } }, + {964, {"MIP09C18.CAM_0", 0, 0, false } }, + {964, {"MIP11C22.CAM_0", 0, 0, false } }, + {964, {"MIP11C27.CAM_0", 0, 0, false } }, + {29772, {"MIP11C29.CAM_0", 0, 0, false } }, + {29700, {"MIP11C29.CAM_1", 0, 0, false } }, + {29748, {"MIP11C29.CAM_2", 0, 0, false } }, + {1736, {"MIP12C02.CAM_0", 0, 0, false } }, + {23280, {"MIP12C02.CAM_0", 0, 0, false } }, + {964, {"MIP12C02.CAM_0", 0, 0, false } }, + {1736, {"MIP12C03.CAM_0", 0, 0, false } }, + {23280, {"MIP12C03.CAM_0", 0, 0, false } }, + {1736, {"MIP12C04.CAM_0", 0, 0, false } }, + {23280, {"MIP12C04.CAM_0", 0, 0, false } }, + {1736, {"MIP12C09.CAM_0", 0, 0, false } }, + {23280, {"MIP12C09.CAM_0", 0, 0, false } }, + {1736, {"MIP12C10.CAM_0", 0, 0, false } }, + {23280, {"MIP12C10.CAM_0", 0, 0, false } }, + {31656, {"RTHROW.BND_0", 0, 0, false } }, + {31392, {"RTHROW.BND_1", 0, 0, false } }, + {31432, {"RTHROW.BND_2", 0, 0, false } }, + {31504, {"RTHROW.BND_3", 0, 0, false } }, + {31576, {"RTHROW.BND_4", 0, 0, false } }, + {31632, {"RTHROW.BND_5", 0, 0, false } }, + {6004, {"RTHROW.BND_0", 0, 0, false } }, + {488, {"RTHROW.BND_0", 0, 0, false } }, + {20900, {"BAYROLL.BAN_0", 0, 0, false } }, + {20788, {"BAYROLL.BAN_1", 0, 0, false } }, + {20812, {"BAYROLL.BAN_2", 0, 0, false } }, + {20824, {"BAYROLL.BAN_3", 0, 0, false } }, + {20836, {"BAYROLL.BAN_4", 0, 0, false } }, + {20848, {"BAYROLL.BAN_5", 0, 0, false } }, + {20872, {"BAYROLL.BAN_6", 0, 0, false } }, + {29772, {"ROCKBAG.BAN_0", 0, 0, false } }, + {29700, {"ROCKBAG.BAN_1", 0, 0, false } }, + {29748, {"ROCKBAG.BAN_2", 0, 0, false } }, + {488, {"PUIROCK.BAN_0", 0, 0, false } }, + {4840, {"TRAPDOOR.BAN_0", 0, 0, false } }, + {4788, {"TRAPDOOR.BAN_1", 0, 0, false } }, + {4800, {"TRAPDOOR.BAN_2", 0, 0, false } }, + {4812, {"TRAPDOOR.BAN_3", 0, 0, false } }, + {748, {"ROPES.BAN_0", 0, 0, false } }, + {148, {"WEB.BAN_0", 0, 0, false } }, + {4808, {"MILIFT.BND_0", 0, 0, false } }, + {4748, {"MILIFT.BND_1", 0, 0, false } }, + {4760, {"MILIFT.BND_2", 0, 0, false } }, + {4784, {"MILIFT.BND_3", 0, 0, false } }, + {1936, {"MILIFT.BND_0", 0, 0, false } }, + {6736, {"DRILL.BAN_0", 0, 0, false } }, + {6676, {"DRILL.BAN_1", 0, 0, false } }, + {6688, {"DRILL.BAN_2", 0, 0, false } }, + {6712, {"DRILL.BAN_3", 0, 0, false } }, + {2688, {"SLAM.BAN_0", 0, 0, false } }, + {2640, {"SLAM.BAN_1", 0, 0, false } }, + {2656, {"SLAM.BAN_2", 0, 0, false } }, + {2672, {"SLAM.BAN_3", 0, 0, false } }, + {15632, {"MINEFAN.BAN_0", 0, 0, false } }, + {96876, {"SLOG.BND_0", 0, 0, false } }, + {96344, {"SLOG.BND_1", 0, 0, false } }, + {96424, {"SLOG.BND_2", 0, 0, false } }, + {96464, {"SLOG.BND_3", 0, 0, false } }, + {96496, {"SLOG.BND_4", 0, 0, false } }, + {96532, {"SLOG.BND_5", 0, 0, false } }, + {96580, {"SLOG.BND_6", 0, 0, false } }, + {96640, {"SLOG.BND_7", 0, 0, false } }, + {96660, {"SLOG.BND_8", 0, 0, false } }, + {96680, {"SLOG.BND_9", 0, 0, false } }, + {96692, {"SLOG.BND_10", 0, 0, false } }, + {96716, {"SLOG.BND_11", 0, 0, false } }, + {96728, {"SLOG.BND_12", 0, 0, false } }, + {96752, {"SLOG.BND_13", 0, 0, false } }, + {96764, {"SLOG.BND_14", 0, 0, false } }, + {96804, {"SLOG.BND_15", 0, 0, false } }, + {15156, {"SLOG.BND_0", 0, 0, false } }, + {15068, {"SLOG.BND_1", 0, 0, false } }, + {15108, {"SLOG.BND_2", 0, 0, false } }, + {15132, {"SLOG.BND_3", 0, 0, false } }, + {39064, {"SLOG.BND_0", 0, 0, false } }, + {38904, {"SLOG.BND_1", 0, 0, false } }, + {38960, {"SLOG.BND_2", 0, 0, false } }, + {12812, {"SLOG.BND_0", 0, 0, false } }, + {12724, {"SLOG.BND_1", 0, 0, false } }, + {12412, {"DOGKNFD.BAN_0", 0, 0, false } }, + {7544, {"DOGBLOW.BAN_0", 0, 0, false } }, + {7504, {"DOGBLOW.BAN_1", 0, 0, false } }, +}; + +static std::vector gPreviewTextures; +static std::vector gExternalPreviewTextures; +static std::vector gPreviewTexturesBounds; +static AssetMeta gExternalPreviewMeta; +static std::vector gLoadedAnimRecords; +static int gActiveToolIndex = 0; +static int gPreviewFPS = 0; +static std::vector gLastPalette; +static int gTableIndex = -1; +static float gElapsedSeconds = 0.0f; +static bool gPingPong = false; +static bool gPonging = false; +static int gResetFrame = 0; +static int gCurrentFrame = 0; +static bool gIsAo = false; +static bool gExternalPreviewEnabled = true; +static AnimRecord gSelectedRecord; +static std::vector gTools; +static std::string gLoadedLevelName = ""; +static bool gHasAeFiles = false; +static bool gHasAoFiles = false; + +// Exporting +static std::thread gExportThread; +static bool gExporting = false; +static float gExportProgress = 0; +static std::string gExportMessage = ""; +static bool gShowExportPreDialog = false; + +// Message Box +static bool gShowMessageBox = false; +static std::string gMessageBoxTitle = ""; +static std::string gMessageBoxText = ""; + +///// Helper Functions ///// + +static void CheckDirectory(std::string path) +{ + std::filesystem::path dir(path); + if (!std::filesystem::exists(dir)) + { + std::filesystem::create_directories(dir); + } +} + +static bool FileExists(std::string filePath) +{ + std::ifstream file(filePath); + bool exists = file.good(); + file.close(); + return exists; +} + +static bool FolderExists(std::string path) +{ + fs::path p(path); + return fs::exists(p) && fs::is_directory(p); +} + +///// + +static void ConvertPalette(const u8* srcPalData, RGBAPixel* dst, s32 palDepth) +{ + const u16* palShortPtr = reinterpret_cast(srcPalData); + for (s32 i = 0; i < palDepth; i++) + { + const u16 oldPixel = palShortPtr[i]; + + dst[i].R = static_cast((((oldPixel >> 0) & 0x1F)) << 3); + dst[i].G = static_cast((((oldPixel >> 5) & 0x1F)) << 3); + dst[i].B = static_cast((((oldPixel >> 10) & 0x1F)) << 3); + dst[i].A = static_cast((((((oldPixel) >> 15) & 0xffff)) ? 127 : 255)); + } + + dst[0] = { 0,0,0,0 }; +} + +static GLuint GenTexture() +{ + GLuint texId = 0; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return texId; +} + +static void LoadLevel(const char* path) +{ + sLvlArchive_5BC520.Free_433130(); + if (!sLvlArchive_5BC520.Open_Archive_432E80(path)) + { + ALIVE_FATAL("Failed to open LVL!"); + } + + // get filename from path + std::string lvlName = path; + auto pos = lvlName.find_last_of('/'); + if (pos != std::string::npos) + { + lvlName = lvlName.substr(pos + 1); + } + gLoadedLevelName = lvlName; + + gLoadedAnimRecords.clear(); + gTableIndex = -1; + + for (int i = 1; i < static_cast(AnimId::Anim_Tester); i++) + { + AnimRecord record; + + if (gIsAo) + { + if (!AnimRecExists(false, static_cast(i))) + { + continue; + } + record = AO::AnimRec(static_cast(i)); + } + else + { + if (!AnimRecExists(true, static_cast(i))) + { + continue; + } + record = AnimRec(static_cast(!gIsAo, i)); + } + + if (record.mBanName == nullptr) + continue; + + if (sLvlArchive_5BC520.Find_File_Record_433160(record.mBanName) == nullptr) + { + continue; + } + + std::string listItemName = static_cast(magic_enum::enum_name(static_cast(i))); + + gLoadedAnimRecords.push_back({ listItemName, record }); + } +} + +static void InitResourceManager() +{ + // Hacky stuff to get the resource manager to load. + sObjectIds_5C1B70.ctor_449AE0(101); + + gBaseGameObject_list_BB47C4 = ae_new>(); + gBaseGameObject_list_BB47C4->ctor_40CA60(50); + + gObjList_drawables_5C1124 = ae_new>(); + gObjList_drawables_5C1124->ctor_40CA60(30); + + gObjList_animations_5C1A24 = ae_new>(); + gObjList_animations_5C1A24->ctor_40CA60(30); + + pResourceManager_5C1BB0 = ae_new(); + pResourceManager_5C1BB0->ctor_464910(); + + ResourceManager::Init_49BCE0(); + + if (FileExists("mi.lvl")) + { + LoadLevel("mi.lvl"); + gIsAo = false; + } + else if (FileExists("r1.lvl")) + { + LoadLevel("r1.lvl"); + gIsAo = true; + } + else + { + ALIVE_FATAL("mi.lvl or r1.lvl not found! Please run this program from the root of the game directory."); + } + + if (FileExists("mi.lvl")) + { + gHasAeFiles = true; + } + if (FileExists("r1.lvl")) + { + gHasAoFiles = true; + } +} + +static Bounds BoundsUnion(Bounds a, Bounds b) +{ + Bounds result; + result.x = min(a.x, b.x); + result.y = min(a.y, b.y); + result.w = max(a.x + a.w, b.x + b.w) - result.x; + result.h = max(a.y + a.h, b.y + b.h) - result.y; + return result; +} + +// Takes an anim and returns the bounds of the animation +static Bounds CalculateAnimationBounds(std::vector& boundsList, int padding = 0) +{ + Bounds bounds = { 0,0,0,0 }; + bool first = true; + + for (auto& frame : boundsList) + { + Bounds frameBounds = { frame.x - padding, frame.y - padding, frame.w + (padding * 2), frame.h + (padding * 2) }; + + if (first) + { + bounds = frameBounds; + first = false; + } + + bounds = BoundsUnion(bounds, frameBounds); + } + + return bounds; +} + +static PreviewTexture DecodedFrameToGL(std::vector& pixelData, int width, int height) +{ + GLuint newTex = GenTexture(); + glBindTexture(GL_TEXTURE_2D, newTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data()); + + return { newTex, width, height }; +} + +static std::vector DecodeFrame(const FrameHeader* pFrameHeader, int * width, int height, std::vector& palette) +{ + bool uncompressed = false; + std::vector pixelData; + pixelData.resize(*width * height); + + std::vector convertedData; + convertedData.resize(*width * height * 3); + + std::vector newData; + + switch (pFrameHeader->field_7_compression_type) + { + case CompressionType::eType_0_NoCompression: + + if (palette.size() == 16) + { + *width = ((int)(ceil(*width / 8.0))) * 8; + pixelData.resize(*width * height); + for (int i = 0; i < *width * height; i++) + { + newData.push_back(reinterpret_cast(&pFrameHeader->field_8_width2)[i] & 0x0f); + newData.push_back((reinterpret_cast(&pFrameHeader->field_8_width2)[i] & 0xf0) >> 4); + } + } + else + { + pixelData.resize(*width * height); + for (int i = 0; i < *width * height; i++) + { + newData.push_back(reinterpret_cast(&pFrameHeader->field_8_width2)[i]); + } + } + convertedData = newData; + break; + case CompressionType::eType_1_NotUsed: + // This isn't in any of the animation data files on disk, therefore can't ever be used. + ALIVE_FATAL("Compression type 1 never expected to be used."); + break; + + case CompressionType::eType_2_ThreeToFourBytes: + CompressionType2_Decompress_40AA50( + reinterpret_cast(&pFrameHeader[1]), + convertedData.data(), + *width * pFrameHeader->field_5_height * 2); + break; + + case CompressionType::eType_3_RLE_Blocks: + if (gIsAo) + { + AO::Decompress_Type_3_4031E0( + (u8*)&pFrameHeader[1], + convertedData.data(), + *(u32*)&pFrameHeader->field_8_width2, + 2 * pFrameHeader->field_5_height * *width); + } + else + { + CompressionType_3Ae_Decompress_40A6A0(reinterpret_cast(&pFrameHeader->field_8_width2), convertedData.data()); + } + + break; + + case CompressionType::eType_4_RLE: + case CompressionType::eType_5_RLE: + CompressionType_4Or5_Decompress_4ABAB0(reinterpret_cast(&pFrameHeader->field_8_width2), convertedData.data()); + + if (palette.size() == 16) + { + *width = ((int)(ceil(*width / 8.0))) * 8; + pixelData.resize(*width * height); + for (int i = 0; i < *width * height; i++) + { + newData.push_back(convertedData[i] & 0x0f); + newData.push_back((convertedData[i] & 0xf0) >> 4); + } + convertedData = newData; + } + + break; + + case CompressionType::eType_6_RLE: + CompressionType6Ae_Decompress_40A8A0(reinterpret_cast(&pFrameHeader->field_8_width2), convertedData.data()); + *width = ((int)(ceil(*width / 8.0))) * 8; + pixelData.resize(*width * height); + for (int i = 0; i < *width * height; i++) + { + newData.push_back(convertedData[i] & 0x0f); + newData.push_back((convertedData[i] & 0xf0) >> 4); + } + convertedData = newData; + break; + + case CompressionType::eType_7_NotUsed: + case CompressionType::eType_8_NotUsed: + ALIVE_FATAL("Decompression 7 and 8 never expected to be used"); + break; + } + + if (!uncompressed) + { + for (unsigned int p = 0; p < pixelData.size(); p++) + { + pixelData[p] = palette[convertedData[p]]; + } + } + + return pixelData; +} + +static std::vector ReadPalette(u8** ppRes, const FrameHeader* pFrameHeader) +{ + u8* pClut = &(*ppRes)[pFrameHeader->field_0_clut_offset]; + + int palDepth = 0; + + switch (pFrameHeader->field_6_colour_depth) + { + case 4: + palDepth = 16; + break; + case 8: + if (*(u32*)pClut != 64) + { + palDepth = 256; + } + else + { + palDepth = 64; + } + break; + case 16: + ALIVE_FATAL("Todo: this"); + break; + } + + std::vector palette; + palette.resize(palDepth); + ConvertPalette(pClut + 4, palette.data(), palDepth); + + return palette; +} + +static bool DumpAnim(std::string path, AnimRecord& rec) +{ + ResourceManager::Free_Resource_Of_Type_49C6B0(ResourceManager::Resource_Animation); + ResourceManager::Free_Resource_Of_Type_49C6B0(ResourceManager::Resource_Palt); + + LOG_INFO("Dumping Anim to " << path); + std::vector textureBoundList; + + if (sLvlArchive_5BC520.Find_File_Record_433160(rec.mBanName) == nullptr) + return false; + + ResourceManager::LoadResourceFile_49C170(rec.mBanName, nullptr); + u8** ppRes = ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Animation, rec.mResourceId, 0, 0); + + if (ppRes == nullptr) + return false; + + AnimationHeader* pAnimHeader = reinterpret_cast(*ppRes + rec.mFrameTableOffset); + gPreviewFPS = pAnimHeader->field_0_fps; + gResetFrame = pAnimHeader->field_4_loop_start_frame; + + if (pAnimHeader->field_0_fps > 100 || pAnimHeader->field_0_fps < 0 || gResetFrame > 100 || gResetFrame < 0 || + pAnimHeader->field_2_num_frames < 0 || pAnimHeader->field_2_num_frames > 255) + { + // These are not valid values, and so something is up. Try not to crash! + return false; + } + + std::vector metaFrameInfo; + + metaFrameInfo.reserve(pAnimHeader->field_2_num_frames); + + // We have to calculate the bounds first. + for (int i = 0; i < pAnimHeader->field_2_num_frames; i++) + { + FrameInfoHeader* pFrameInfo = reinterpret_cast(*ppRes + pAnimHeader->mFrameOffsets[i]); + const FrameHeader* pFrameHeader = reinterpret_cast(&(*ppRes)[pFrameInfo->field_0_frame_header_offset]); + + metaFrameInfo.push_back(MetaFrameInfo{(int) pFrameHeader->field_4_width, (int) pFrameHeader->field_5_height, pFrameInfo->field_8_data.offsetAndRect.mOffset.x, pFrameInfo->field_8_data.offsetAndRect.mOffset.y}); + + int width = static_cast(ceil(pFrameHeader->field_4_width / 4.0f) * 4); + int height = pFrameHeader->field_5_height; + int offsetX = pFrameInfo->field_8_data.offsetAndRect.mOffset.x; + int offsetY = pFrameInfo->field_8_data.offsetAndRect.mOffset.y; + + if (gIsAo) + { + offsetX = PsxToPCX(offsetX); + } + textureBoundList.push_back({ offsetX, offsetY, width, height }); + } + + Bounds bounds = CalculateAnimationBounds(textureBoundList, 8); + CheckDirectory(path); + AssetMeta metaInfo = { + gIsAo ? "AO" : "AE", // game + 2 * 8, // padding + metaFrameInfo, + bounds.w, // width + bounds.h * 2, // height + (-bounds.x), // offset x + ((-bounds.y) * 2), // offset y, + static_cast(pAnimHeader->field_2_num_frames) // frame count + }; + metaInfo.SaveJSONToFile(path + "/meta.json", true); + + // Now we start to convert stuff + for (int i = 0; i < pAnimHeader->field_2_num_frames; i++) + { + FrameInfoHeader* pFrameInfo = reinterpret_cast(*ppRes + pAnimHeader->mFrameOffsets[i]); + const FrameHeader* pFrameHeader = reinterpret_cast(&(*ppRes)[pFrameInfo->field_0_frame_header_offset]); + + + std::vector palette = ReadPalette(ppRes, pFrameHeader); + + if (rec.mPalOverride != PalId::Default) + { + PalRecord palOverride = PalRec(rec.mPalOverride); + + if (sLvlArchive_5BC520.Find_File_Record_433160(palOverride.mBanName) == nullptr) + { + LOG_ERROR("Couldn't find palette resource ban " << palOverride.mBanName); + ResourceManager::FreeResource_49C330(ppRes); + return false; + } + + ResourceManager::LoadResourceFile_49C170(palOverride.mBanName, nullptr); + u8** ppPalResource = ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Palt, palOverride.mResourceId, 1, 1); + + ConvertPalette(*ppPalResource + 4, palette.data(), static_cast(palette.size())); + + ResourceManager::FreeResource_49C330(ppPalResource); + } + + gLastPalette = palette; + + int width = static_cast(ceil(pFrameHeader->field_4_width / 4.0f) * 4); + int height = pFrameHeader->field_5_height; + + auto frameConverted = DecodeFrame(pFrameHeader, &width, height, palette); + + int offsetX = pFrameInfo->field_8_data.offsetAndRect.mOffset.x; + int offsetY = pFrameInfo->field_8_data.offsetAndRect.mOffset.y; + + if (gIsAo) + { + offsetX = PsxToPCX(offsetX); + } + + std::vector dstImage; + dstImage.resize(bounds.w * (bounds.h * 2)); + + int copyX = offsetX - bounds.x; + int copyY = (offsetY - bounds.y) * 2; + + int srcY = 0; + for (int y = copyY; y < copyY + (height * 2); y++) + { + memcpy(&dstImage[(y * bounds.w) + copyX], &frameConverted[srcY * width], width * sizeof(RGBAPixel)); + memcpy(&dstImage[((y + 1) * bounds.w) + copyX], &frameConverted[srcY * width], width * sizeof(RGBAPixel)); + + y++; + srcY++; + } + + stbi_write_png((path + "/" + std::to_string(i) + ".png").c_str(), bounds.w, bounds.h * 2, 4, dstImage.data(), bounds.w * 4); + } + + ResourceManager::FreeResource_49C330(ppRes); + + return true; +} + +static GLuint LoadImageToGL(std::string path, int * width, int * height) +{ + int x = 0, y = 0, comp = 0; + + std::vector fileData; + + // Try to keep all paths and filenames lowercase for our linux friends. + std::ifstream file(path, std::ios::binary); + + if (file.is_open()) + { + fileData = std::vector((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + } + + const unsigned char* data = stbi_load_from_memory(fileData.data(), static_cast(fileData.size()), &x, &y, &comp, 4); + + GLuint newTexture = 0; + glGenTextures(1, &newTexture); + + glBindTexture(GL_TEXTURE_2D, newTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + stbi_image_free((void*)data); + + if (width != nullptr) + *width = x; + + if (height != nullptr) + *height = y; + + return newTexture; +} + +static void LoadAnimExternalPreview(AnimRecord& rec) +{ + for (auto& cache : gExternalPreviewTextures) + { + glDeleteTextures(1, &cache.id); + } + + gExternalPreviewTextures.clear(); + + std::string folderName = std::string(magic_enum::enum_name(rec.mId)); + std::string folderPath = "hd/sprites/" + folderName; + + // check if folder path exists + if (!FolderExists(folderPath)) + { + return; + } + + gExternalPreviewMeta.LoadJSONFromFile(folderPath + "/meta.json"); + + for (int i = 0; i < gExternalPreviewMeta.frame_count; i++) + { + gExternalPreviewTextures.push_back({ LoadImageToGL(folderPath + "/" + std::to_string(i) + ".png", nullptr, nullptr) }); + } +} + +static void LoadAnimPreview(AnimRecord& rec) +{ + for (auto& cache : gPreviewTextures) + { + glDeleteTextures(1, &cache.id); + } + + LoadAnimExternalPreview(rec); + + gPreviewTextures.clear(); + gPreviewTexturesBounds.clear(); + gLastPalette.clear(); + gCurrentFrame = 0; + gPonging = false; + gPingPong = false; + gResetFrame = 0; + + if (sLvlArchive_5BC520.Find_File_Record_433160(rec.mBanName) == nullptr) + return; + + ResourceManager::LoadResourceFile_49C170(rec.mBanName, nullptr); + u8** ppRes = ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Animation, rec.mResourceId, 1, 1); + + if (ppRes == nullptr) + return; + + AnimationHeader* pAnimHeader = reinterpret_cast(*ppRes + rec.mFrameTableOffset); + gPreviewFPS = pAnimHeader->field_0_fps; + gResetFrame = pAnimHeader->field_4_loop_start_frame; + + if (pAnimHeader->field_0_fps > 100 || pAnimHeader->field_0_fps < 0 || gResetFrame > 100 || gResetFrame < 0 || + pAnimHeader->field_2_num_frames < 0 || pAnimHeader->field_2_num_frames > 255) + { + // These are not valid values, and so something is up. Try not to crash! + return; + } + + for (int i = 0; i < pAnimHeader->field_2_num_frames; i++) + { + FrameInfoHeader* pFrameInfo = reinterpret_cast(*ppRes + pAnimHeader->mFrameOffsets[i]); + const FrameHeader* pFrameHeader = reinterpret_cast(&(*ppRes)[pFrameInfo->field_0_frame_header_offset]); + + std::vector palette = ReadPalette(ppRes, pFrameHeader); + + if (rec.mPalOverride != PalId::Default) + { + PalRecord palOverride = PalRec(rec.mPalOverride); + + if (sLvlArchive_5BC520.Find_File_Record_433160(palOverride.mBanName) == nullptr) + { + LOG_ERROR("Couldn't find palette resource ban " << palOverride.mBanName); + return; + } + + ResourceManager::LoadResourceFile_49C170(palOverride.mBanName, nullptr); + u8** ppPalResource = ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Palt, palOverride.mResourceId, 1, 1); + + ConvertPalette(*ppPalResource + 4, palette.data(), static_cast(palette.size())); + + ResourceManager::FreeResource_49C330(ppPalResource); + } + + gLastPalette = palette; + + int width = static_cast(ceil(pFrameHeader->field_4_width / 4.0f) * 4); + int height = pFrameHeader->field_5_height; + + auto frameConverted = DecodeFrame(pFrameHeader, &width, height, palette); + PreviewTexture newTexture = DecodedFrameToGL(frameConverted, width, height); + + newTexture.offsetX = pFrameInfo->field_8_data.offsetAndRect.mOffset.x; + newTexture.offsetY = pFrameInfo->field_8_data.offsetAndRect.mOffset.y; + + if (gIsAo) + { + newTexture.offsetX = PsxToPCX(newTexture.offsetX); + } + + gPreviewTextures.push_back(newTexture); + gPreviewTexturesBounds.push_back({ newTexture.offsetX, newTexture.offsetY, width, height }); + } + + ResourceManager::FreeResource_49C330(ppRes); +} + +static void ToolTip(std::string& message) +{ + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(message.c_str()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); +} + +static void AppAnimationViewer(ImGuiWindowFlags flags, float /*elapsedTime*/) +{ + ImVec2 windowPadding = ImGui::GetStyle().WindowPadding; + + if (ImGui::Begin("Animation Viewer", 0, flags)) + { + ImVec2 windowSize = ImGui::GetContentRegionAvail(); + float listBoxSize = 268.0f; + if (ImGui::BeginChild("##list_frame", { listBoxSize , windowSize.y }, false)) + { + static std::string searchQuery; + ImGui::PushItemWidth(-1); + ImGui::InputTextWithHint("##list_search", "Search...", &searchQuery); + ImGui::PopItemWidth(); + + if (ImGui::BeginListBox("##resource_list", ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y - windowPadding.y))) + { + for (auto i = 0; i < static_cast(gLoadedAnimRecords.size()); i++) + { + if (!searchQuery.empty() && StringToLowerCase(gLoadedAnimRecords[i].name).find(StringToLowerCase(searchQuery)) == std::string::npos) + continue; + + if (ImGui::Selectable(gLoadedAnimRecords[i].name.c_str(), i == gTableIndex)) + { + gTableIndex = i; + gSelectedRecord = gLoadedAnimRecords[i].record; + LoadAnimPreview(gLoadedAnimRecords[i].record); + } + } + + ImGui::EndListBox(); + } + } + ImGui::EndChild(); + + ImGui::SameLine(); + + if (ImGui::BeginChild("##content_panel", { windowSize.x - listBoxSize , windowSize.y }, false)) + { + ImVec2 subContentViewSize = ImGui::GetContentRegionAvail(); + float animFramesChildSize = 150; + + if (ImGui::BeginChild("##frame_view", { -FLT_MIN , animFramesChildSize - windowPadding.y }, true)) + { + float frameHeight = ImGui::GetContentRegionAvail().y; + + int i = 0; + for (auto t : gPreviewTextures) + { + ImGui::SetCursorPosY((frameHeight * 0.5f) - t.height); + ImGui::Image(reinterpret_cast(t.id), { (float)(t.width), (float)(t.height * 2) }, { 0,0 }, { 1,1 }, { 1,1,1,1 }, ((gCurrentFrame == i) ? ImVec4{ 1, 1, 0.5f, 0.7f } : ImVec4{ 1, 1, 1, 0.5f })); + ImGui::SameLine(); + + if (ImGui::IsItemHovered()) + { + std::string tipText = "Frame: " + std::to_string(i + 1); + tipText += " Size: " + std::to_string(t.width) + "x" + std::to_string(t.height); + tipText += " Offset: " + std::to_string(t.offsetX) + ", " + std::to_string(t.offsetY); + + ToolTip(tipText); + } + + i++; + } + } + ImGui::EndChild(); + + if (ImGui::BeginChild("##preview", { subContentViewSize.x , subContentViewSize.y - animFramesChildSize }, true) && gTableIndex >= 0) + { + + static float scale = 1.0f; + static bool gShowBounds = false; + ImVec2 windowPos = ImGui::GetCursorPos(); + ImVec2 availableWindowSpace = ImGui::GetContentRegionAvail(); + + // Animation Scale buttons + if (ImGui::Button("-")) + { + if (scale > 1) + { + scale--; + } + } + ImGui::SameLine(); + if (ImGui::Button("+")) + { + scale++; + } + ImGui::SameLine(); + ImGui::Text("|"); + ImGui::SameLine(); + ImGui::Checkbox("Show Bounds", &gShowBounds); + ImGui::SameLine(); + ImGui::Text("|"); + ImGui::SameLine(); + + if (gExternalPreviewTextures.size() > 0) + { + ImGui::Checkbox("Show External", &gExternalPreviewEnabled); + } + else + { + ImGui::TextDisabled("No External"); + } + + ImGui::SameLine(); + ImGui::Text("|"); + ImGui::SameLine(); + + std::string animExportPath = "hd/sprites/" + gLoadedAnimRecords[gTableIndex].name; + + if (ImGui::Button("Export")) + { + // check if export folder (animExportPath) already exists + if (FolderExists(animExportPath)) + { + ImGui::OpenPopup("Are you sure?##export_single_sure"); + } + { + DumpAnim(animExportPath, gSelectedRecord); + } + } + + if (ImGui::BeginPopup("Are you sure?##export_single_sure", ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("There is an existing export of the current animation.\n Are you sure you want to overwrite it?"); + + if (ImGui::Button("Yes")) + { + DumpAnim(animExportPath, gSelectedRecord); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("No")) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + const int toolSectionWidth = 300; + + if (gExternalPreviewTextures.size() > 0) + { + ImGui::SetCursorPos({ windowPos.x + (availableWindowSpace.x - toolSectionWidth), windowPos.y }); + ImGui::BeginChild("##external_editor", { toolSectionWidth, -FLT_MIN }, false); + ImGui::Separator(); + + // edit all the attributes of the selected meta + static int step = 1; + static int stepFast = 5; + ImGui::InputScalarN("Ref Image Size", ImGuiDataType_S32, &gExternalPreviewMeta.size_width, 2, &step, &stepFast); + ImGui::InputScalarN("Offset", ImGuiDataType_S32, &gExternalPreviewMeta.offset_x, 2, &step, &stepFast); + + ImGui::Separator(); + + if (ImGui::Button("Save")) + { + gExternalPreviewMeta.SaveJSONToFile("hd/sprites/" + std::string(magic_enum::enum_name(gSelectedRecord.mId)) + "/meta.json"); + } + ImGui::EndChild(); + } + + static ImVec2 previewWindowOffset(0, 0); + + ImGui::SetCursorPos(windowPos); + ImVec2 windowScreenPos = ImGui::GetCursorScreenPos(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + + // Render palette colors to the bottom of the preview + ImVec2 paletteSize = { availableWindowSpace.x / gLastPalette.size(), 32 }; + + int i = 0; + for (auto p : gLastPalette) + { + ImVec2 offset = { windowScreenPos.x + (i * (availableWindowSpace.x / gLastPalette.size())), windowScreenPos.y + availableWindowSpace.y - paletteSize.x }; + + draw_list->AddRectFilled(offset, { offset.x + paletteSize.x, offset.y + paletteSize.x }, IM_COL32(p.R, p.G, p.B, p.A)); + + i++; + } + + // Animate and render the anim + if (gPreviewTextures.size() > 0) + { + Bounds bounds = CalculateAnimationBounds(gPreviewTexturesBounds, 8); + bounds.y *= 2; + bounds.h *= 2; + + // Render the background reference lines + float floorOffset = (availableWindowSpace.y / 2) - ((bounds.h * scale) / 2) - (bounds.y * scale); + + draw_list->AddLine({ windowScreenPos.x + (availableWindowSpace.x / 2), windowScreenPos.y }, { windowScreenPos.x + (availableWindowSpace.x / 2), windowScreenPos.y + availableWindowSpace.y }, IM_COL32(255, 255, 255, 60), 2.0f); + draw_list->AddLine({ windowScreenPos.x, windowScreenPos.y + floorOffset }, { windowScreenPos.x + availableWindowSpace.x, windowScreenPos.y + floorOffset }, IM_COL32(255, 255, 255, 120), 2.0f); + + static float frameSkipDelta = 0; + + frameSkipDelta += gElapsedSeconds * 30; + + if (frameSkipDelta >= gPreviewFPS) // todo get proper frame delays + { + frameSkipDelta = 0; + + if (gPonging) + { + gCurrentFrame--; + + if (gCurrentFrame < 0) + { + gCurrentFrame = static_cast(gPreviewTextures.size()); + } + } + else + { + gCurrentFrame++; + + if (gCurrentFrame >= static_cast(gPreviewTextures.size())) + { + gCurrentFrame = gResetFrame; + } + } + } + + float offsetXScaled = gPreviewTextures[gCurrentFrame].offsetX * scale; + float offsetYScaled = gPreviewTextures[gCurrentFrame].offsetY * scale; + + ImVec2 imagePos = { (availableWindowSpace.x / 2.0f) + offsetXScaled, floorOffset + (offsetYScaled * 2) }; + ImVec2 imageSize = { (float)(gPreviewTextures[gCurrentFrame].width * scale), (float)(gPreviewTextures[gCurrentFrame].height * 2 * scale) }; + draw_list->AddImage(reinterpret_cast(gPreviewTextures[gCurrentFrame].id), { windowScreenPos.x + imagePos.x, windowScreenPos.y + imagePos.y }, + { windowScreenPos.x + imagePos.x + imageSize.x, windowScreenPos.y + imagePos.y + imageSize.y }); + + if (gExternalPreviewTextures.size() > 0 && gExternalPreviewEnabled) + { + float offsetXScaled_External = gExternalPreviewMeta.offset_x * scale; + float offsetYScaled_External = gExternalPreviewMeta.offset_y * scale; + + ImVec2 imagePos_External = { (availableWindowSpace.x / 2.0f) - offsetXScaled_External, floorOffset - offsetYScaled_External }; + ImVec2 imageSize_External = { (float)(gExternalPreviewMeta.size_width * scale), (float)(gExternalPreviewMeta.size_height * scale) }; + + if (gCurrentFrame >= static_cast(gExternalPreviewTextures.size())) + { + draw_list->AddText({ windowScreenPos.x + imagePos_External.x, windowScreenPos.y + imagePos_External.y }, IM_COL32(255, 0, 0, 255), "MISSING TEXTURE FILE!"); + } + else + { + draw_list->AddImage(reinterpret_cast(gExternalPreviewTextures[gCurrentFrame].id), { windowScreenPos.x + imagePos_External.x, windowScreenPos.y + imagePos_External.y }, + { windowScreenPos.x + imagePos_External.x + imageSize_External.x, windowScreenPos.y + imagePos_External.y + imageSize_External.y }); + } + } + + if (gShowBounds) + { + draw_list->AddRect({ windowScreenPos.x + imagePos.x, windowScreenPos.y + imagePos.y }, + { windowScreenPos.x + imagePos.x + imageSize.x, windowScreenPos.y + imagePos.y + imageSize.y }, IM_COL32(255, 255, 255, 200)); + + draw_list->AddRect( + { windowScreenPos.x + (availableWindowSpace.x / 2.0f) + (bounds.x * scale), windowScreenPos.y + floorOffset + (bounds.y * scale) }, + { windowScreenPos.x + (availableWindowSpace.x / 2.0f) + ((bounds.x + bounds.w) * scale), windowScreenPos.y + floorOffset + ((bounds.y + bounds.h) * scale) }, + IM_COL32(50, 255, 50, 200)); + } + } + } + ImGui::EndChild(); + } + ImGui::EndChild(); + + } + ImGui::End(); +} + +static void ShowMessageBox(std::string title, std::string message) +{ + gShowMessageBox = true; + gMessageBoxTitle = title; + gMessageBoxText = message; +} + +static void export_thread() +{ + std::vector dumpList; + std::vector failedDumpList; + int dumpedAnimsCount = 0; + + int levelCount = sizeof(AeLvls) / sizeof(AeLvls[0]); + float baseLevelPercent = 1.0f / levelCount; + const LevelInfo* pLvlInfos = AeLvls; + + if (gIsAo) + { + pLvlInfos = AoLvls; + levelCount = sizeof(AoLvls) / sizeof(AoLvls[0]); + baseLevelPercent = 1.0f / levelCount; + } + + for(int l = 0; l < levelCount; l++) + { + gExportMessage = "Loading LVL " + std::string(pLvlInfos[l].lvl_file); + LoadLevel(pLvlInfos[l].lvl_file); + + int i = -1; + for (auto& record : gLoadedAnimRecords) + { + if (!gExporting) // cancel exporting + return; + + i++; + gExportProgress = (baseLevelPercent * l) + (baseLevelPercent * (i / (float)gLoadedAnimRecords.size())); + + // if we already have this record, skip it + if (std::find(dumpList.begin(), dumpList.end(), record.name) != dumpList.end()) + { + continue; + } + + gExportMessage = "Exporting " + record.name; + + if (DumpAnim("hd/sprites/" + record.name, record.record)) + { + dumpedAnimsCount++; + } + else + { + failedDumpList.push_back(record.name); + } + + dumpList.push_back(record.name); + } + } + + gExportMessage = "Exported " + std::to_string(dumpedAnimsCount) + " animations!"; + + if (failedDumpList.size() > 0) + { + gExportMessage += "\nFailed to export the following anims:\n"; + + for (auto& anim : failedDumpList) + { + gExportMessage += anim + "\n"; + } + } + + gExporting = false; + gExportProgress = 1.0f; +} + +static void ui_popup_export() +{ + // imgui popup that shows the export progress and lets the user cancel it + // it also shows the current export message + + if (gExporting) + { + ImGui::OpenPopup("Export Progress"); + } + + if (ImGui::BeginPopupModal("Export Progress", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) + { + ImGui::Text("Exporting..."); + ImGui::ProgressBar(gExportProgress, ImVec2(500.0f, 0.0f)); + + ImGui::Text("%s", gExportMessage.c_str()); + + if (gExportProgress >= 1.0f) + { + if (ImGui::Button("Close")) + { + ImGui::CloseCurrentPopup(); + } + } + else + { + if (ImGui::Button("Cancel")) + { + gExporting = false; + gExportThread.detach(); + ImGui::CloseCurrentPopup(); + } + } + + ImGui::EndPopup(); + } + + // create a 'are you sure?' popup that lets the user stop before the export starts + if (ImGui::BeginPopupModal("Are you sure?##export_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) + { + ImGui::Text("Are you sure you want to export the selected animations?"); + ImGui::Text("Exporting all will overwrite any existing files you have!"); + ImGui::Text("This means ones you have edited. Backup first!"); + + if (ImGui::Button("Yes, I have backed up my edited files.")) + { + gExporting = true; + gExportProgress = 0; + + // check if the export thread is running already and if so, detach it + if (gExportThread.joinable()) + { + gExportThread.detach(); + } + + gExportThread = std::thread(export_thread); + ImGui::CloseCurrentPopup(); + } + + if (ImGui::Button("No - Get me outta here!")) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + +static void ui_loop() +{ + if (ImGui::BeginMainMenuBar()) + { + if (ImGui::BeginMenu("Tools")) + { + for (unsigned int i = 0; i < gTools.size(); i++) + { + const AppTool& tool = gTools[i]; + + if (ImGui::MenuItem(tool.name)) + { + gActiveToolIndex = i; + } + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Load Level")) + { + if (gHasAoFiles && ImGui::BeginMenu("Oddysee")) + { + for (auto& lvl : AoLvls) + { + if (ImGui::MenuItem(lvl.lvl_file)) + { + gIsAo = true; + LoadLevel(lvl.lvl_file); + } + } + ImGui::EndMenu(); + } + if (gHasAeFiles && ImGui::BeginMenu("Exoddus")) + { + for (auto& lvl : AeLvls) + { + if (ImGui::MenuItem(lvl.lvl_file)) + { + gIsAo = false; + LoadLevel(lvl.lvl_file); + } + } + ImGui::EndMenu(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Utilities")) + { + if (ImGui::MenuItem("Export All Anims")) + { + gShowExportPreDialog = true; + } + + if (ImGui::MenuItem("Copy old->new name map")) + { + std::string newMap = ""; + auto records = GetAnimRecords(); + for(auto oldEntry : frametableMatches) + { + auto frameTableOffset = oldEntry.first; + auto frameData = oldEntry.second; + // converts file name. ex: "EXPLODE.BND_0" to "EXPLODE.BND" + std::string fileName = frameData.file_prefix.substr(0, frameData.file_prefix.find_last_of('_')); + + for(auto r : records) + { + AnimDetails& details = (gIsAo ? r.mAOData : r.mAEData); + + if (details.mBanName != nullptr && details.mBanName == fileName && details.mFrameTableOffset == frameTableOffset) + { + std::string newName = std::string(magic_enum::enum_name(r.mId)); + newMap += frameData.file_prefix + "_* -> " + newName + "\n"; + } + } + } + + // imgui copy to clipboard + ImGui::SetClipboardText(newMap.c_str()); + + ShowMessageBox("Copied!", "Copied map to clipboard!"); + } + + ImGui::EndMenu(); + } + + if (ImGui::MenuItem("About")) + { + ShowMessageBox("About", "Asset Tool v0.2 by mlgthatsme (milk man)"); + } + + ImGui::EndMainMenuBar(); + } + + if (gShowMessageBox) + { + gShowMessageBox = false; + ImGui::OpenPopup((gMessageBoxTitle + "##message_box_title").c_str()); + } + + if (ImGui::BeginPopupModal((gMessageBoxTitle + "##message_box_title").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) + { + ImGui::Text(gMessageBoxText.c_str()); + + if (ImGui::Button("Cool")) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + if (gShowExportPreDialog) + { + ImGui::OpenPopup("Are you sure?##export_popup"); + gShowExportPreDialog=false; + } + + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->WorkPos); + ImGui::SetNextWindowSize(viewport->WorkSize); + + const ImGuiWindowFlags appFlags = kImGuiFullscreenFlags; + + if (gActiveToolIndex >= 0) + { + gTools[gActiveToolIndex].update_func(appFlags, gElapsedSeconds); + } + + ui_popup_export(); +} + +void main_loop() +{ + // init tools + gTools.push_back({ "Animation Viewer", AppAnimationViewer }); + gTools.push_back({ "FMV Viewer", AppVideoViewer }); + + gTools.push_back({ "ImGui Demo Window", [](ImGuiWindowFlags /*flags*/, float) { ImGui::ShowDemoWindow(); } }); + + // init all game resource management + InitResourceManager(); + + SDL_Window* window = NULL; + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) + { + fprintf(stderr, "could not initialize sdl2: %s\n", SDL_GetError()); + return; + } + + window = SDL_CreateWindow( + "Asset Extractor", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + 1280, 720, + SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE + ); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + // Create context + SDL_GLContext mContext = SDL_GL_CreateContext(window); + glewInit(); + + if (mContext == NULL) + { + LOG_ERROR("OpenGL context could not be created! SDL Error: " << SDL_GetError()); + return; + } + else + { + // Initialize GLEW + glewExperimental = GL_TRUE; + GLenum glewError = glewInit(); + if (glewError != GLEW_OK) + { + LOG_ERROR("Error initializing GLEW! " << glewGetErrorString(glewError)); + } + + // Use Vsync + if (SDL_GL_SetSwapInterval(1) < 0) + { + LOG_ERROR("Warning: Unable to set VSync! SDL Error: " << SDL_GetError()); + } + } + + ImGui::CreateContext(); + + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + + // Setup IMGUI for texture debugging + ImGui_ImplSDL2_InitForOpenGL(window, mContext); + ImGui_ImplOpenGL3_Init("#version 150"); + + bool quit = false; + SDL_Event e; + while (!quit) + { + while (SDL_PollEvent(&e) != 0) + { + if (e.type == SDL_QUIT) + { + quit = true; + } + + ImGui_ImplSDL2_ProcessEvent(&e); + } + + glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(window); + ImGui::NewFrame(); + + ui_loop(); + + SDL_SetWindowTitle(window, ("Asset Tool (" + gLoadedLevelName + ") | " + ((gIsAo) ? "Oddysee" : "Exoddus") + " mode").c_str()); + + ImGui::EndFrame(); + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + SDL_GL_SwapWindow(window); + + static int lastTime = SDL_GetTicks(); + int currentTime = SDL_GetTicks(); + int deltaTime = currentTime - lastTime; + + gElapsedSeconds = deltaTime / 1000.0f; + + lastTime = currentTime; + } + ImGui_ImplSDL2_Shutdown(); + + SDL_GL_DeleteContext(mContext); + SDL_DestroyWindow(window); + + SDL_PauseAudio(1); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + + SDL_Quit(); + return; +} + +s32 main(s32 /*argc*/, char_type** /*argv*/) +{ +#if _WIN32 + ::AllocConsole(); + ::freopen("CONOUT$", "w", stdout); + ::SetConsoleTitleA("Debug Console"); + ::SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); + RedirectIoStream(true); +#endif + main_loop(); + return 0; +} diff --git a/Source/Tools/asset_tool/asset_video.hpp b/Source/Tools/asset_tool/asset_video.hpp new file mode 100644 index 000000000..7145b3355 --- /dev/null +++ b/Source/Tools/asset_tool/asset_video.hpp @@ -0,0 +1,565 @@ +#include +#include +#include +#include + +// pipe to ffmpeg +#include +#include + +#include "Masher.hpp" + +namespace fs = std::filesystem; + +struct AssetFMV_Video +{ + int width; + int height; + int fps; +}; + +struct AssetFMV_Audio +{ + int audioChannels; + int audioSampleRate; +}; + +struct AssetFMVParams +{ + AssetFMV_Video video; + AssetFMV_Audio audio; + std::string outputPath; +}; + +static HANDLE videoPipeWrite; +static HANDLE audioPipeWrite; +static HANDLE videoPipeRead; +static HANDLE audioPipeRead; +static HANDLE videoProcess; +static HANDLE audioProcess; + +static AssetFMVParams gParameters; + +static std::vector gFrameBuffer; +static bool gFMVHasFrames = false; +static float gFrameTime = 0; +static int gFmvIndex = -1; + +static Masher* gMasher = nullptr; +static GLuint gMasherTexture = 0; + +#define GL_TO_IMGUI_TEX(v) *reinterpret_cast(&v) + +static bool g_bHasAudio_5CA234; +static s32 g_fmv_audio_sample_offset_5CA238; +static s32 g_fmv_num_read_frames_5CA23C; +static bool g_bNoAudioOrAudioError_5CA1F4; +static s32 g_fmv_single_audio_frame_size_in_samples_5CA240; +static s32 g_current_audio_offset_5CA1F0; +static s32 g_fmv_num_played_audio_frames_5CA1FC; +static s32 g_oldBufferPlayPos_5CA22C; + +static std::thread gFMVExportThread; +static bool gFMVExporting = false; +static float gFMVExportProgress = 0; +static std::string gFMVExportMessage = ""; +static bool gFMVShowExportPreDialog = false; + +HANDLE CreateProcessWithPipe(const std::string& command, HANDLE* pPipeWrite, HANDLE* pPipeRead) +{ + SECURITY_ATTRIBUTES sa = { sizeof(sa) }; + sa.lpSecurityDescriptor = nullptr; + sa.bInheritHandle = TRUE; + + CreatePipe(pPipeRead, pPipeWrite, &sa, 0); + + SetHandleInformation(*pPipeWrite, HANDLE_FLAG_INHERIT, 0); + + STARTUPINFOA si = { sizeof(si) }; + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = *pPipeRead; + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION pi = {}; + + CreateProcess(nullptr, (LPSTR)command.c_str(), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi); + + return pi.hProcess; +} + +int ffmpeg_begin(AssetFMVParams paramters) +{ + gParameters = paramters; + std::string pix_fmt = "rgba"; + std::string video_size = std::to_string(paramters.video.width) + "x" + std::to_string(paramters.video.height); + std::string frame_rate = std::to_string(paramters.video.fps); + + std::string audio_channels = std::to_string(paramters.audio.audioChannels); + std::string audio_sample_rate = std::to_string(paramters.audio.audioSampleRate); + + std::string args_video_input = " -f rawvideo -pix_fmt " + pix_fmt + " -thread_queue_size 64 -video_size " + video_size + " -r " + frame_rate + " -i pipe:0 -vf \"pad = ceil(iw / 2) * 2:ceil(ih / 2) * 2\" -preset medium -pix_fmt yuv420p -crf 10 "; + std::string args_audio_input = " -f s16le -thread_queue_size 1024 -ar " + audio_sample_rate + " -ac " + audio_channels + " -i pipe:0 "; + std::string ffmpeg_path = "ffmpeg.exe"; + std::string ffmpeg_args = args_audio_input + args_video_input + "-y " + paramters.outputPath; + + videoProcess = CreateProcessWithPipe(ffmpeg_path + args_video_input + "-y " + paramters.outputPath + ".video.mp4", &videoPipeWrite, &videoPipeRead); + audioProcess = CreateProcessWithPipe(ffmpeg_path + args_audio_input + "-y " + paramters.outputPath + ".audio.m4a", &audioPipeWrite, &audioPipeRead); + + return 0; +} + +// pushes a frame in rgba8888 format to ffmpeg +int ffmpeg_push_frame(u8* frameBuffer, int width, int height) +{ + DWORD bytesWritten; + WriteFile(videoPipeWrite, frameBuffer, width * height * 4, &bytesWritten, nullptr); + + return 0; +} + +int ffmpeg_push_audio(u8* audioBuffer, int lengthBytes) +{ + DWORD bytesWritten; + WriteFile(audioPipeWrite, audioBuffer, lengthBytes, &bytesWritten, nullptr); + + return 0; +} + +int ffmpeg_end() +{ + CloseHandle(videoPipeWrite); + CloseHandle(audioPipeWrite); + + // wait for video process to finish + WaitForSingleObject(videoProcess, INFINITE); + + // wait for audio process to finish + WaitForSingleObject(audioProcess, INFINITE); + + // create a process to merge the video and audio + std::string ffmpeg_path = "ffmpeg.exe"; + std::string ffmpeg_args = " -i " + gParameters.outputPath + ".video.mp4 -i " + gParameters.outputPath + ".audio.m4a -c:v copy -c:a aac -strict experimental -map 0:v:0 -map 1:a:0 -y " + gParameters.outputPath + ".mp4"; + + videoProcess = CreateProcessWithPipe(ffmpeg_path + ffmpeg_args, &videoPipeWrite, &videoPipeRead); + + // wait for ffmpeg to finish + WaitForSingleObject(videoProcess, INFINITE); + + // delete the temporary files + std::filesystem::remove(gParameters.outputPath + ".video.mp4"); + std::filesystem::remove(gParameters.outputPath + ".audio.m4a"); + + return 0; +} + +static GLuint CreateVideoPlayerTexture() +{ + GLuint texId = 0; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return texId; +} + +s8 CC Mash_DecompressAudio() +{ + if (!g_bHasAudio_5CA234) + { + return 1; + } + + u32 audioBufferStartOffset = 0; + g_fmv_audio_sample_offset_5CA238 = 0; + + // Keep reading frames till we have >= number of interleaved so that we have 1 full frame + if (g_fmv_num_read_frames_5CA23C < gMasher->field_2C_audio_header.field_10_num_frames_interleave) + { + while (Masher::ReadNextFrameToMemory_4EAC30(gMasher)) + { + const int bitsPerSample = (gMasher->field_2C_audio_header.field_0_audio_format & 2) ? 16 : 8; + const int channels = (gMasher->field_2C_audio_header.field_0_audio_format & 1) ? 2 : 1; + + void* pDecompressedAudioFrame = Masher::GetDecompressedAudioFrame_4EAC60(gMasher); + + if (pDecompressedAudioFrame) + { + ffmpeg_push_audio(reinterpret_cast(pDecompressedAudioFrame), (bitsPerSample / 8) * g_fmv_single_audio_frame_size_in_samples_5CA240 * channels); + + /*if (GetSoundAPI().SND_LoadSamples( + &g_fmv_sound_entry_5CA208, + g_fmv_audio_sample_offset_5CA238, + (u8*)pDecompressedAudioBuffer, + g_fmv_single_audio_frame_size_in_samples_5CA240)) + { + g_bNoAudioOrAudioError_5CA1F4 = 1; + }*/ + } + + g_fmv_audio_sample_offset_5CA238 += g_fmv_single_audio_frame_size_in_samples_5CA240; + audioBufferStartOffset = g_fmv_audio_sample_offset_5CA238; + g_fmv_num_read_frames_5CA23C++; + + if (g_fmv_num_read_frames_5CA23C >= gMasher->field_2C_audio_header.field_10_num_frames_interleave) + { + break; + } + } + } + + if (g_fmv_num_read_frames_5CA23C >= gMasher->field_2C_audio_header.field_10_num_frames_interleave) + { + // Update the offset to the size of the first demuxed frame + g_current_audio_offset_5CA1F0 = audioBufferStartOffset; + if (!g_bNoAudioOrAudioError_5CA1F4) + { + // Sound entry is created and populated with 1 frame, play it + /*if (FAILED(GetSoundAPI().SND_PlayEx(&fmv_sound_entry_5CA208, 116, 116, 1.0, 0, 1, 100))) + { + g_bNoAudioOrAudioError_5CA1F4 = 1; + }*/ + } + g_fmv_num_played_audio_frames_5CA1FC = 0; + g_oldBufferPlayPos_5CA22C = 0; + return 1; + } + + return 0; +} + +static bool InitDDVPlayback(const std::string& filePath, bool ffmpegExport) +{ + g_bHasAudio_5CA234 = 0; + g_fmv_audio_sample_offset_5CA238 = 0; + g_fmv_num_read_frames_5CA23C = 0; + g_bNoAudioOrAudioError_5CA1F4 = 0; + g_fmv_single_audio_frame_size_in_samples_5CA240 = 0; + g_current_audio_offset_5CA1F0 = 0; + g_fmv_num_played_audio_frames_5CA1FC = 0; + g_oldBufferPlayPos_5CA22C = 0; + + gFrameBuffer.resize(640 * 480 * 4); + + if (gMasher != nullptr) + { + delete gMasher; + gMasher = nullptr; + } + + gMasher = new Masher(); + + gMasherTexture = CreateVideoPlayerTexture(); + + gMasher->Init_4E6770(filePath.c_str()); + + AssetFMVParams params; + params.video.fps = 15; + params.video.width = gMasher->field_14_video_header.field_4_width; + params.video.height = gMasher->field_14_video_header.field_8_height; + params.outputPath = filePath; + + params.audio.audioSampleRate = gMasher->field_2C_audio_header.field_4_samples_per_second; + params.audio.audioChannels = (gMasher->field_2C_audio_header.field_0_audio_format & 1) ? 2 : 1; + + if (ffmpegExport) + { + ffmpeg_begin(params); + } + + g_bHasAudio_5CA234 = ((u32)gMasher->field_4_ddv_header.field_4_contains >> 1) & 1; + g_fmv_single_audio_frame_size_in_samples_5CA240 = gMasher->field_2C_audio_header.field_C_single_audio_frame_size; + //const auto fmv_sound_entry_size = g_fmv_single_audio_frame_size_in_samples_5CA240 * (gMasher->field_2C_audio_header.field_10_num_frames_interleave + 6); + + g_bNoAudioOrAudioError_5CA1F4 = 0; + if (g_bHasAudio_5CA234 && gMasher->field_2C_audio_header.field_0_audio_format) + { + //if (GetSoundAPI().SND_New( + // &fmv_sound_entry_5CA208, + // fmv_sound_entry_size, + // pMasher_audio_header_5CA1E0->field_4_samples_per_second, + // (pMasher_audio_header_5CA1E0->field_0_audio_format & 2) != 0 ? 16 : 8, + // (pMasher_audio_header_5CA1E0->field_0_audio_format & 1) | 6) + // < 0) + //{ + // // SND_New failed + // fmv_sound_entry_5CA208.field_4_pDSoundBuffer = nullptr; + // g_bNoAudioOrAudioError_5CA1F4 = 1; + //} + } + else + { + // Source DDV has no audio + g_bNoAudioOrAudioError_5CA1F4 = 1; + } + + if (Mash_DecompressAudio() && gMasher->ReadNextFrame_4E6B30() && gMasher->ReadNextFrame_4E6B30()) + { + return true; + } + else + { + return false; + } +} + +static bool StepDDVPlayback(bool ffmpegExport) +{ + gMasher->VideoFrameDecode_Raw(gFrameBuffer.data()); + + + if (ffmpegExport) + { + ffmpeg_push_frame(gFrameBuffer.data(), gMasher->field_14_video_header.field_4_width, gMasher->field_14_video_header.field_8_height); + } + else + { + glBindTexture(GL_TEXTURE_2D, gMasherTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gMasher->field_14_video_header.field_4_width, gMasher->field_14_video_header.field_8_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, gFrameBuffer.data()); + } + + g_fmv_num_read_frames_5CA23C++; + + if (!g_bNoAudioOrAudioError_5CA1F4) + { + const int bitsPerSample = (gMasher->field_2C_audio_header.field_0_audio_format & 2) ? 16 : 8; + const int channels = (gMasher->field_2C_audio_header.field_0_audio_format & 1) ? 2 : 1; + + void* pDecompressedAudioFrame = Masher::GetDecompressedAudioFrame_4EAC60(gMasher); + + if (pDecompressedAudioFrame) + { + if (ffmpegExport) + { + ffmpeg_push_audio(reinterpret_cast(pDecompressedAudioFrame), (bitsPerSample / 8) * g_fmv_single_audio_frame_size_in_samples_5CA240 * channels); + } + + //// Push new samples into the buffer + //if (GetSoundAPI().SND_LoadSamples(&fmv_sound_entry_5CA208, fmv_audio_sample_offset_5CA238, (u8*)pDecompressedAudioFrame, fmv_single_audio_frame_size_in_samples_5CA240) < 0) + //{ + // // Reload with data fail + // bNoAudioOrAudioError_5CA1F4 = 1; + //} + } + else + { + //if (GetSoundAPI().SND_Clear(&fmv_sound_entry_5CA208, fmv_audio_sample_offset_5CA238, fmv_single_audio_frame_size_in_samples_5CA240) < 0) + //{ + // // Reload with silence on failure or no data + // bNoAudioOrAudioError_5CA1F4 = 1; + //} + } + + g_fmv_audio_sample_offset_5CA238 += g_fmv_single_audio_frame_size_in_samples_5CA240; + } + const s32 bMoreFrames = gMasher->ReadNextFrame_4E6B30(); + + if (!bMoreFrames) + { + if (ffmpegExport) + { + ffmpeg_end(); + } + + return false; + } + + return true; +} + +static void StartPlayback(const std::string& filePath, bool ffmpegExport) +{ + gFrameTime = 0; + gFMVHasFrames = InitDDVPlayback(filePath, ffmpegExport); +} + +static std::string gFMVExportFilePath; +static std::vector ddvFiles; + +static void ExportDDVThreadFunc() +{ + int currentFrame = 0; + StartPlayback(gFMVExportFilePath, true); + while(StepDDVPlayback(true)) + { + currentFrame++; + gFMVExportProgress = (float)currentFrame / gMasher->field_4_ddv_header.field_C_number_of_frames; + gFMVExportMessage = gFMVExportFilePath + "\nExporting frame " + std::to_string(currentFrame) + " of " + std::to_string(gMasher->field_4_ddv_header.field_C_number_of_frames); + } + + gFMVExportProgress = 1; + gFMVExportMessage = "Done!"; + gFMVExporting = false; +} + +static void ExportAllDDVThreadFunc() +{ + int ddvCount = ddvFiles.size(); + + for (int i = 0; i < ddvCount; i++) + { + const std::string& ddvFileName = ddvFiles[i]; + + int currentFrame = 0; + StartPlayback(ddvFileName, true); + while (StepDDVPlayback(true)) + { + currentFrame++; + float p = ((float)currentFrame / gMasher->field_4_ddv_header.field_C_number_of_frames) / (float)ddvCount; + gFMVExportProgress = (i * (1 / (float)ddvCount)) + p; + gFMVExportMessage = ddvFileName + "\nExporting frame " + std::to_string(currentFrame) + " of " + std::to_string(gMasher->field_4_ddv_header.field_C_number_of_frames); + } + } + + gFMVExportProgress = 1; + gFMVExportMessage = "Done!"; + gFMVExporting = false; +} + +static void AppVideoViewer(ImGuiWindowFlags flags, float elapsedTime) +{ + IO_Init_494230(); + + if (gFMVHasFrames && !gFMVExporting) + { + gFrameTime += elapsedTime; + + if (gFrameTime >= 1.0f / 15.0f) + { + gFrameTime = 0; + gFMVHasFrames = StepDDVPlayback(false); + } + } + + ImVec2 windowPadding = ImGui::GetStyle().WindowPadding; + + std::string ddvDir = "movies/"; + + if (ImGui::Begin("Video Viewer", 0, flags)) + { + ImVec2 windowSize = ImGui::GetContentRegionAvail(); + float listBoxSize = 268.0f; + if (ImGui::BeginChild("##fmv_list_frame", { listBoxSize , windowSize.y }, false)) + { + static std::string searchQuery; + ImGui::PushItemWidth(-1); + ImGui::InputTextWithHint("##fmv_list_search", "Search...", &searchQuery); + ImGui::PopItemWidth(); + + // get list of .ddv files in movie directory + + + if (ddvFiles.size() == 0) + { + for (const auto& entry : fs::directory_iterator(ddvDir)) + { + if (StringToLowerCase(entry.path().extension().string()) == ".ddv") + { + ddvFiles.push_back(entry.path().string()); + } + } + } + + + if (ImGui::BeginListBox("##fmv_list", ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y - windowPadding.y))) + { + for (auto i = 0; i < static_cast(ddvFiles.size()); i++) + { + const std::string& ddvFileName = ddvFiles[i]; + + if (!searchQuery.empty() && StringToLowerCase(ddvFileName).find(StringToLowerCase(searchQuery)) == std::string::npos) + continue; + + if (ImGui::Selectable(ddvFileName.c_str(), i == gFmvIndex)) + { + gFmvIndex = i; + StartPlayback(ddvFileName, false); + } + } + + ImGui::EndListBox(); + } + } + ImGui::EndChild(); + + ImGui::SameLine(); + + if (ImGui::BeginChild("##fmv_preview", { windowSize.x - listBoxSize , windowSize.y }, false)) + { + if (ImGui::Button("Export")) + { + if (gFmvIndex >= 0) + { + gFMVExporting = true; + gFMVExportProgress = 0; + gFMVExportFilePath = ddvFiles[gFmvIndex]; + + // check if the export thread is running already and if so, detach it + if (gFMVExportThread.joinable()) + { + gFMVExportThread.detach(); + } + + gFMVExportThread = std::thread(ExportDDVThreadFunc); + ImGui::CloseCurrentPopup(); + } + } + ImGui::SameLine(); + if (ImGui::Button("Export All")) + { + gFMVExporting = true; + gFMVExportProgress = 0; + + // check if the export thread is running already and if so, detach it + if (gFMVExportThread.joinable()) + { + gFMVExportThread.detach(); + } + + gFMVExportThread = std::thread(ExportAllDDVThreadFunc); + ImGui::CloseCurrentPopup(); + } + ImVec2 previewSize = ImGui::GetContentRegionAvail(); + ImGui::Image(GL_TO_IMGUI_TEX(gMasherTexture), previewSize); + } + ImGui::EndChild(); + + } + ImGui::End(); + + if (gFMVExporting) + { + ImGui::OpenPopup("FMV Export"); + } + + if (ImGui::BeginPopupModal("FMV Export", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) + { + ImGui::Text("Exporting FMV..."); + ImGui::ProgressBar(gFMVExportProgress, ImVec2(500.0f, 0.0f)); + + ImGui::Text("%s", gFMVExportMessage.c_str()); + + if (gFMVExportProgress >= 1.0f) + { + if (ImGui::Button("Close")) + { + ImGui::CloseCurrentPopup(); + } + } + else + { + if (ImGui::Button("Cancel")) + { + gFMVExporting = false; + gFMVExportThread.detach(); + ImGui::CloseCurrentPopup(); + } + } + + ImGui::EndPopup(); + } +} \ No newline at end of file diff --git a/Source/Tools/asset_tool/resource.h b/Source/Tools/asset_tool/resource.h new file mode 100644 index 000000000..7051c9281 --- /dev/null +++ b/Source/Tools/asset_tool/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Resource.rc +// +#define IDI_ICON1 101 +#define IDI_MAIN_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Source/Tools/asset_tool/resource.rc b/Source/Tools/asset_tool/resource.rc new file mode 100644 index 000000000..6f37a1cde --- /dev/null +++ b/Source/Tools/asset_tool/resource.rc @@ -0,0 +1,102 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +#include "../../Source/AliveLibCommon/relive_config.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#if !defined(__MINGW32__) +# include "winres.h" +#endif + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United Kingdom) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _MSC_VER +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#endif + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#if !defined(__MINGW32__)""\r\n" + "# include ""winres.h""\r\n" + "#endif""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAIN_ICON ICON "app.ico" +#endif // English (United Kingdom) resources +///////////////////////////////////////////////////////////////////////////// + +#ifndef BUILD_NUMBER + #define BUILD_NUMBER 1 +#endif + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,BUILD_NUMBER,0 + PRODUCTVERSION 1,0,BUILD_NUMBER,0 +{ + BLOCK "StringFileInfo" + { + BLOCK "040904b0" + { + VALUE "CompanyName", "ALIVE Team.\0" + VALUE "FileDescription", "RELIVE Asset Tool (CI origin: " CI_PROVIDER ")\0" + VALUE "FileVersion", "1.0." STR(BUILD_NUMBER) "\0" + VALUE "OriginalFilename", "asset_tool.exe\0" + VALUE "ProductName", "RELIVE Asset Tool\0" + VALUE "ProductVersion", "1.0." STR(BUILD_NUMBER) "\0" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } +} + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/Source/relive/Exe.cpp b/Source/relive/Exe.cpp index 78494b609..7d75f2b8c 100644 --- a/Source/relive/Exe.cpp +++ b/Source/relive/Exe.cpp @@ -200,6 +200,7 @@ static bool CheckRequiredGameFilesExist(GameType gameType, bool showError) static s32 AOMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, s32 nShowCmd) { LOG_INFO("AO standalone starting..."); + gIsGameAE = false; AO::Static_Inits_AO(); PopulateAutoSplitterVars(GameType::eAo); return AO::WinMain_48EF50(hInstance, hPrevInstance, lpCmdLine, nShowCmd); @@ -208,6 +209,7 @@ static s32 AOMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, static s32 AEMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, s32 nShowCmd) { LOG_INFO("AE standalone starting..."); + gIsGameAE = true; // In the real game these are called before main, but shouldn't really matter in this case Static_Inits_AE(); PopulateAutoSplitterVars(GameType::eAe); diff --git a/options.cmake b/options.cmake index 3cb55d185..03f5ecc2b 100644 --- a/options.cmake +++ b/options.cmake @@ -16,5 +16,5 @@ option(ORIGINAL_PS1_BEHAVIOR "Fixes bugs in the PSX Emu layer / Gameplay to matc option(ORIGINAL_GAME_FIXES "Fixes ALL known gameplay bugs" ON) option(ORIGINAL_GAME_FIX_AUTO_TURN "Fixes the auto-turn bug commonly used in speedruns" OFF) option(ORIGINAL_GAME_FIX_DEATH_DELAY_AO "Fixes the death delay glitch commonly used in speedruns" OFF) -option(RENDERER_OPENGL "Use OpenGL hardware accelerated rendering." OFF) +option(RENDERER_OPENGL "Use OpenGL hardware accelerated rendering." ON) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Source/relive_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/Source/AliveLibCommon/relive_config.h)