diff --git a/.gitmodules b/.gitmodules index 903eba43a..2634cfc1d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "rapidjson"] path = rapidjson url = https://github.com/Tencent/rapidjson.git +[submodule "NESEmulator/fixNES"] + path = NESEmulator/fixNES + url = https://github.com/jackoalan/fixNES.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d9906d5a..d90a0d2cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,7 +199,9 @@ add_definitions(-DZE_ATHENA_TYPES=1) set(ZEUS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/specter/zeus/include) include_directories(${ATHENA_INCLUDE_DIR} ${LOGVISOR_INCLUDE_DIR} ${HECL_INCLUDE_DIR} ${NOD_INCLUDE_DIR} ${ZEUS_INCLUDE_DIR} ${BOO_INCLUDE_DIR} ${AMUSE_INCLUDE_DIR} - ${SPECTER_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + ${SPECTER_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/NESEmulator) +add_subdirectory(NESEmulator) add_subdirectory(DataSpec) add_subdirectory(kabufuda) include_directories(${KABUFUDA_INCLUDE_DIR}) diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index 1847acb65..8c9cb72a3 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -97,7 +97,7 @@ target_link_libraries(urde UrdeLocales UrdeIcons UrdeBadging - RuntimeCommon + RuntimeCommon NESEmulator specter specter-fonts freetype ${DATA_SPEC_LIBS} hecl-full hecl-blender-addon athena-core nod logvisor athena-libyaml amuse boo diff --git a/NESEmulator/CMakeLists.txt b/NESEmulator/CMakeLists.txt new file mode 100644 index 000000000..eb83fd9e8 --- /dev/null +++ b/NESEmulator/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(${CMAKE_SOURCE_DIR}/DataSpec ${CMAKE_SOURCE_DIR}/Runtime ${CMAKE_CURRENT_SOURCE_DIR}) +file(GLOB MAPPER_SRCS fixNES/mapper/*.c) +add_library(NESEmulator CNESEmulator.hpp CNESEmulator.cpp CNESShader.hpp CNESShader.cpp malloc.h + fixNES/apu.c fixNES/audio_fds.c fixNES/audio_mmc5.c fixNES/audio_vrc6.c fixNES/audio_vrc7.c + fixNES/audio_n163.c fixNES/audio_s5b.c fixNES/cpu.c fixNES/ppu.c fixNES/mem.c fixNES/input.c + fixNES/mapper.c fixNES/mapperList.c fixNES/fm2play.c fixNES/vrc_irq.c ${MAPPER_SRCS}) diff --git a/NESEmulator/CNESEmulator.cpp b/NESEmulator/CNESEmulator.cpp new file mode 100644 index 000000000..061e349f6 --- /dev/null +++ b/NESEmulator/CNESEmulator.cpp @@ -0,0 +1,804 @@ +#include "CNESEmulator.hpp" +#include "CNESShader.hpp" +#include "CGameState.hpp" +#include "Input/CFinalInput.hpp" +#include "logvisor/logvisor.hpp" +#include +#include +#include +#include +#include +#include "malloc.h" +#include +#include +#include +#include + +static urde::MP1::CNESEmulator* EmulatorInst = nullptr; + +extern "C" +{ + +#include "fixNES/mapper.h" +#include "fixNES/cpu.h" +#include "fixNES/ppu.h" +#include "fixNES/mem.h" +#include "fixNES/input.h" +#include "fixNES/fm2play.h" +#include "fixNES/apu.h" +#include "fixNES/audio_fds.h" +#include "fixNES/audio_vrc7.h" +#include "fixNES/mapper_h/nsf.h" + +/* + * Copyright (C) 2017 FIX94 + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +#define WINDOWS_BUILD 1 +#define DEBUG_HZ 0 +#define DEBUG_MAIN_CALLS 0 +#define DEBUG_KEY 0 +#define DEBUG_LOAD_INFO 1 + +std::chrono::steady_clock::time_point s_tp = std::chrono::steady_clock::now(); +static std::chrono::milliseconds::rep GetTickCount() +{ + return std::chrono::duration_cast(std::chrono::steady_clock::now() - s_tp).count(); +} + +static const char *VERSION_STRING = "fixNES Alpha v1.0.5"; +static char window_title[256]; +static char window_title_pause[256]; + +enum { + FTYPE_UNK = 0, + FTYPE_NES, + FTYPE_NSF, + FTYPE_FDS, + FTYPE_QD, +#if ZIPSUPPORT + FTYPE_ZIP, +#endif +}; + +static void nesEmuFdsSetup(uint8_t *src, uint8_t *dst); + +static int emuFileType = FTYPE_UNK; +static char emuFileName[1024]; +static uint8_t *emuNesROM = NULL; +static uint32_t emuNesROMsize = 0; +static char emuSaveName[1024]; +static uint8_t *emuPrgRAM = NULL; +static uint32_t emuPrgRAMsize = 0; +//used externally +uint32_t textureImage[0xF000]; +bool nesPause = false; +bool ppuDebugPauseFrame = false; +bool doOverscan = true; +bool nesPAL = false; +bool nesEmuNSFPlayback = false; + +static bool inPause = false; +static bool inOverscanToggle = false; +static bool inResize = false; +static bool inDiskSwitch = false; +static bool inReset = false; + +#if WINDOWS_BUILD +//#include +//typedef bool (APIENTRY *PFNWGLSWAPINTERVALEXTPROC) (int interval); +//PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; +#if DEBUG_HZ +static int emuFrameStart = 0; +static int emuTimesCalled = 0; +static int emuTotalElapsed = 0; +#endif +#if DEBUG_MAIN_CALLS +static int emuMainFrameStart = 0; +static int emuMainTimesCalled = 0; +static int emuMainTimesSkipped = 0; +static int emuMainTotalElapsed = 0; +#endif +#endif + +#define DOTS 341 + +#define VISIBLE_DOTS 256 +#define VISIBLE_LINES 240 + +static uint32_t linesToDraw = VISIBLE_LINES; +static const uint32_t visibleImg = VISIBLE_DOTS*VISIBLE_LINES*4; +static uint8_t scaleFactor = 2; +static bool emuSaveEnabled = false; +static bool emuFdsHasSideB = false; +static uint32_t mainLoopRuns; +static uint32_t mainLoopPos; +static uint16_t ppuCycleTimer; +static uint16_t cpuCycleTimer; +//from input.c +extern uint8_t inValReads[8]; +//from mapper.c +extern bool mapperUse78A; +//from m32.c +extern bool m32_singlescreen; + +static volatile bool emuRenderFrame = false; + +//used externally +bool emuSkipVsync = false; +bool emuSkipFrame = false; + +//static uint32_t mCycles = 0; +static uint16_t mainClock = 1; +static uint16_t apuClock = 1; +static uint16_t ppuClock = 1; +static uint16_t vrc7Clock = 1; + +extern bool fdsSwitch; + +int audioUpdate() +{ + if (!EmulatorInst) + return 0; + return EmulatorInst->audioUpdate(); +} + +} + +namespace urde::MP1 +{ + +bool CNESEmulator::EmulatorConstructed = false; +static logvisor::Module Log("CNESEmulator"); + +#define NESEMUP_ROM_OFFSET 0xa3f8 + +#define METROID_PAL false +#define METROID_MAPPER 1 +#define METROID_SAVE_ENABLED false +#define METROID_TRAINER false +#define METROID_PRG_SIZE (8 * 0x4000) +#define METROID_CHR_SIZE (0 * 0x2000) +#define METROID_PRG_RAM_SIZE 0x2000 + +CNESEmulator::CNESEmulator() +{ + if (EmulatorConstructed) + Log.report(logvisor::Fatal, "Attempted constructing more than 1 CNESEmulator"); + EmulatorConstructed = true; + + CDvdFile NESEmuFile("NESemuP.rel"); + if (NESEmuFile) + { + m_nesEmuPBuf.reset(new u8[0x20000]); + m_dvdReq = NESEmuFile.AsyncSeekRead(m_nesEmuPBuf.get(), 0x20000, ESeekOrigin::Begin, NESEMUP_ROM_OFFSET); + } + else + { + Log.report(logvisor::Fatal, "Unable to open NESemuP.rel"); + } +} + +void CNESEmulator::InitializeEmulator() +{ + emuNesROMsize = 0x20000; + emuNesROM = (uint8_t*)malloc(emuNesROMsize); + DecompressROM(m_nesEmuPBuf.get(), emuNesROM); + m_nesEmuPBuf.reset(); + + puts(VERSION_STRING); + strcpy(window_title, VERSION_STRING); + memset(textureImage,0,visibleImg); + emuFileType = FTYPE_UNK; + memset(emuFileName,0,1024); + memset(emuSaveName,0,1024); + + nesPAL = METROID_PAL; + uint8_t mapper = METROID_MAPPER; + emuSaveEnabled = METROID_SAVE_ENABLED; + bool trainer = METROID_TRAINER; + uint32_t prgROMsize = METROID_PRG_SIZE; + uint32_t chrROMsize = METROID_CHR_SIZE; + emuPrgRAMsize = METROID_PRG_RAM_SIZE; + emuPrgRAM = (uint8_t*)malloc(emuPrgRAMsize); + uint8_t *prgROM = emuNesROM; + if(trainer) + { + memcpy(emuPrgRAM+0x1000,prgROM,0x200); + prgROM += 512; + } + uint8_t *chrROM = NULL; + if(chrROMsize) + { + chrROM = emuNesROM+prgROMsize; + if(trainer) chrROM += 512; + } + apuInitBufs(); + cpuInit(); + ppuInit(); + memInit(); + apuInit(); + inputInit(); + ppuSetNameTblVertical(); +#if DEBUG_LOAD_INFO + printf("Used Mapper: %i\n", mapper); + printf("PRG: 0x%x bytes PRG RAM: 0x%x bytes CHR: 0x%x bytes\n", prgROMsize, emuPrgRAMsize, chrROMsize); +#endif + if(!mapperInit(mapper, prgROM, prgROMsize, emuPrgRAM, emuPrgRAMsize, chrROM, chrROMsize)) + { + printf("Mapper init failed!\n"); + free(emuNesROM); + return; + } +#if DEBUG_LOAD_INFO + printf("Trainer: %i Saving: %i VRAM Mode: Vertical\n", trainer, emuSaveEnabled); +#endif + sprintf(window_title, "%s NES - %s\n", nesPAL ? "PAL" : "NTSC", VERSION_STRING); + + sprintf(window_title_pause, "%s (Pause)", window_title); +#if WINDOWS_BUILD + #if DEBUG_HZ + emuFrameStart = GetTickCount(); + #endif + #if DEBUG_MAIN_CALLS + emuMainFrameStart = GetTickCount(); + #endif +#endif + cpuCycleTimer = nesPAL ? 16 : 12; + //do full frame per update loop + ppuCycleTimer = nesPAL ? 5 : 4; + mainLoopRuns = nesPAL ? DOTS*ppuCycleTimer : DOTS*ppuCycleTimer; + //mainLoopRuns *= ppuLinesTotal; + mainLoopPos = mainLoopRuns; + + CGraphics::CommitResources([this](boo::IGraphicsDataFactory::Context& ctx) + { + m_texture = ctx.newDynamicTexture(VISIBLE_DOTS, linesToDraw, + boo::TextureFormat::RGBA8, + boo::TextureClampMode::ClampToEdgeNearest); + Vert verts[4] = + { + {{-1.f, -1.f, 0.f}, {0.f, 1.f}}, + {{-1.f, 1.f, 0.f}, {0.f, 0.f}}, + {{ 1.f, -1.f, 0.f}, {1.f, 1.f}}, + {{ 1.f, 1.f, 0.f}, {1.f, 0.f}}, + }; + m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, verts, sizeof(Vert), 4); + m_uniBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(Uniform), 1); + m_shadBind = CNESShader::BuildShaderDataBinding(ctx, m_vbo, m_uniBuf, m_texture); + return true; + }); + + //double useFreq = 223740; + double useFreq = apuGetFrequency(); + m_booVoice = CAudioSys::GetVoiceEngine()->allocateNewStereoVoice(useFreq, this); + m_booVoice->start(); + uint32_t apuBufSz = apuGetBufSize(); + m_audioBufBlock.reset(new u8[apuBufSz * NUM_AUDIO_BUFFERS]); + for (int i=0 ; istop(); + m_booVoice.reset(); + apuDeinitBufs(); + if(emuNesROM != NULL) + { + if(!nesEmuNSFPlayback && fdsEnabled) + { + FILE *save = fopen(emuSaveName, "wb"); + if(save) + { + if(emuFdsHasSideB) + fwrite(emuNesROM,1,0x20000,save); + else + fwrite(emuNesROM,1,0x10000,save); + fclose(save); + } + } + free(emuNesROM); + } + emuNesROM = NULL; + if(emuPrgRAM != NULL) + { + if(emuSaveEnabled) + { + FILE *save = fopen(emuSaveName, "wb"); + if(save) + { + fwrite(emuPrgRAM,1,emuPrgRAMsize,save); + fclose(save); + } + } + free(emuPrgRAM); + } + emuPrgRAM = NULL; + //printf("Bye!\n"); + + EmulatorInst = nullptr; +} + +CNESEmulator::~CNESEmulator() +{ + if (EmulatorInst) + DeinitializeEmulator(); + EmulatorConstructed = false; +} + +int CNESEmulator::audioUpdate() +{ + uint8_t *data = apuGetBuf(); + if(data != NULL && m_procBufs) + { + --m_procBufs; + memmove(m_audioBufs[m_headBuf], data, apuGetBufSize()); + //printf("PUSH\n"); + ++m_headBuf; + if (m_headBuf == NUM_AUDIO_BUFFERS) + m_headBuf = 0; + } + + //if (!m_procBufs) + //printf("OVERRUN\n"); + + return m_procBufs; +} + +static const size_t AudioFrameSz = 2 * sizeof(int16_t); + +size_t CNESEmulator::supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data) +{ + size_t remFrames = frames; + while (remFrames) + { + if (m_posInBuf == apuGetBufSize()) + { + ++m_tailBuf; + if (m_tailBuf == NUM_AUDIO_BUFFERS) + m_tailBuf = 0; + m_posInBuf = 0; + ++m_procBufs; + //printf("POP\n"); + } + + if (m_procBufs == NUM_AUDIO_BUFFERS) + { + memset(data, 0, remFrames * AudioFrameSz); + //printf("UNDERRUN\n"); + return frames; + } + + size_t copySz = std::min(apuGetBufSize() - m_posInBuf, remFrames * AudioFrameSz); + memmove(data, m_audioBufs[m_tailBuf] + m_posInBuf, copySz); + data += copySz / sizeof(int16_t); + m_posInBuf += copySz; + remFrames -= copySz / AudioFrameSz; + } + return frames; +} + +#define CATCHUP_SKIP 1 +#if CATCHUP_SKIP +static int catchupFrames = 0; +#endif + +void CNESEmulator::NesEmuMainLoop() +{ + int start = GetTickCount(); + int loopCount = 0; + do + { + if((!emuSkipVsync && emuRenderFrame) || nesPause) + { +#if (WINDOWS_BUILD && DEBUG_MAIN_CALLS) + emuMainTimesSkipped++; +#endif + //printf("LC RENDER: %d\n", loopCount); + m_texture->load(textureImage, visibleImg); + emuRenderFrame = false; +#if CATCHUP_SKIP + if (catchupFrames) + --catchupFrames; + else +#endif + break; + } + ++loopCount; + if(mainClock == cpuCycleTimer) + { + //runs every 8th cpu clock + if(apuClock == 8) + { + if(!apuCycle()) + { +#if (WINDOWS_BUILD && DEBUG_MAIN_CALLS) + emuMainTimesSkipped++; +#endif +#if CATCHUP_SKIP + catchupFrames = 0; +#endif + //printf("LC SKIP\n"); + break; + } + apuClock = 1; + } + else + apuClock++; + //runs every cpu cycle + apuClockTimers(); + //main CPU clock + if(!cpuCycle()) + exit(EXIT_SUCCESS); + //mapper related irqs + if(mapperCycle != NULL) + mapperCycle(); + //mCycles++; + //channel timer updates + apuLenCycle(); + mainClock = 1; + } + else + mainClock++; + if(ppuClock == ppuCycleTimer) + { + if(!ppuCycle()) + exit(EXIT_SUCCESS); + if(ppuDrawDone()) + { + //printf("%i\n",mCycles); + //mCycles = 0; + emuRenderFrame = true; + if(fm2playRunning()) + fm2playUpdate(); +#if (WINDOWS_BUILD && DEBUG_HZ) + emuTimesCalled++; + int end = GetTickCount(); + emuTotalElapsed += end - emuFrameStart; + if(emuTotalElapsed >= 1000) + { + printf("\r%iHz ", emuTimesCalled); + emuTimesCalled = 0; + emuTotalElapsed = 0; + } + emuFrameStart = end; +#endif + if(ppuDebugPauseFrame) + { + ppuDebugPauseFrame = false; + nesPause = true; + } + if(nesEmuNSFPlayback) + nsfVsync(); + } + ppuClock = 1; + } + else + ppuClock++; + if(fdsEnabled) + fdsAudioMasterUpdate(); + if(vrc7enabled) + { + if(vrc7Clock == 432) + { + vrc7AudioCycle(); + vrc7Clock = 1; + } + else + vrc7Clock++; + } + + if ((loopCount % 5000) == 0 && GetTickCount() - start >= 14) + { +#if CATCHUP_SKIP + if (catchupFrames < 50) + ++catchupFrames; +#endif + break; + } + } + while(true); + + int end = GetTickCount(); + printf("%dms\n", end - start); + +#if (WINDOWS_BUILD && DEBUG_MAIN_CALLS) + emuMainTimesCalled++; + int end = GetTickCount(); + //printf("%dms\n", end - start); + emuMainTotalElapsed += end - emuMainFrameStart; + if(emuMainTotalElapsed >= 1000) + { + printf("\r%i calls, %i skips ", emuMainTimesCalled, emuMainTimesSkipped); + fflush(stdout); + emuMainTimesCalled = 0; + emuMainTimesSkipped = 0; + emuMainTotalElapsed = 0; + } + emuMainFrameStart = end; +#endif +} + +static void nesEmuFdsSetup(uint8_t *src, uint8_t *dst) +{ + memcpy(dst, src, 0x38); + memcpy(dst+0x3A, src+0x38, 2); + uint16_t cDiskPos = 0x3E; + uint16_t cROMPos = 0x3A; + do + { + if(src[cROMPos] != 0x03) + break; + memcpy(dst+cDiskPos, src+cROMPos, 0x10); + uint16_t copySize = (*(uint16_t*)(src+cROMPos+0xD))+1; + cDiskPos+=0x12; + cROMPos+=0x10; + memcpy(dst+cDiskPos, src+cROMPos, copySize); + cDiskPos+=copySize+2; + cROMPos+=copySize; + } while(cROMPos < 0xFFDC && cDiskPos < 0xFFFF); + printf("%04x -> %04x\n", cROMPos, cDiskPos); +} + +void CNESEmulator::DecompressROM(u8* dataIn, u8* dataOut, u32 dataOutLen, u8 descrambleSeed, + u32 checkDataLen, u32 checksumMagic) +{ + for (int i=0 ; i<256 ; ++i) + { + descrambleSeed += dataIn[i]; + dataIn[i] = descrambleSeed; + } + + for (int i=0 ; i<128 ; ++i) + std::swap(dataIn[255 - i], dataIn[i]); + + struct BitstreamState + { + u32 x0_remBits = 0; + u32 x4_lastByte; + u8* x8_ptr; + BitstreamState(u8* ptr) : x8_ptr(ptr) {} + } bState = {dataIn + 256}; + + u8* dataOutAlpha = dataOut; + while (dataOutLen != 0) + { + u32 r12 = 0; + + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + u32 r11 = r12 << 1; + if (bState.x4_lastByte & 0x80) + r11 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + + if (r11 == 1) + { + r12 = 0; + + for (int i=0 ; i<4 ; ++i) + { + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + r12 <<= 1; + if (bState.x4_lastByte & 0x80) + r12 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + r12 <<= 1; + if (bState.x4_lastByte & 0x80) + r12 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + } + + *dataOutAlpha++ = dataIn[r12 + 73]; + } + else + { + r12 = 0; + + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + r11 = r12 << 1; + if (bState.x4_lastByte & 0x80) + r11 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + + if (r11 == 1) + { + r12 = 0; + + for (int i=0 ; i<3 ; ++i) + { + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + r12 <<= 1; + if (bState.x4_lastByte & 0x80) + r12 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + r12 <<= 1; + if (bState.x4_lastByte & 0x80) + r12 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + } + + *dataOutAlpha++ = dataIn[r12 + 9]; + } + else + { + r12 = 0; + + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + r11 = r12 << 1; + if (bState.x4_lastByte & 0x80) + r11 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + + if (r11 == 1) + { + r12 = 0; + + for (int i=0 ; i<3 ; ++i) + { + if (bState.x0_remBits == 0) + { + bState.x4_lastByte = *bState.x8_ptr; + bState.x8_ptr += 1; + bState.x0_remBits = 8; + } + + r12 <<= 1; + if (bState.x4_lastByte & 0x80) + r12 |= 1; + + bState.x4_lastByte <<= 1; + bState.x0_remBits -= 1; + } + + *dataOutAlpha++ = dataIn[r12 + 1]; + } + else + { + *dataOutAlpha++ = dataIn[0]; + } + } + } + + --dataOutLen; + } + + u32 tmpWord = 0; + for (int i=0 ; i> 1) ^ checksumMagic; + else + tmpWord = (tmpWord >> 1); + } + } + + dataOut[checkDataLen - 1] = (tmpWord >> 8) & 0xff; + dataOut[checkDataLen - 2] = tmpWord & 0xff; +} + +void CNESEmulator::ProcessUserInput(const CFinalInput& input, int) +{ + inValReads[BUTTON_UP] = input.DDPUp() | input.DLAUp(); + inValReads[BUTTON_DOWN] = input.DDPDown() | input.DLADown(); + inValReads[BUTTON_LEFT] = input.DDPLeft() | input.DLALeft(); + inValReads[BUTTON_RIGHT] = input.DDPRight() | input.DLARight(); + inValReads[BUTTON_A] = input.DA(); + inValReads[BUTTON_B] = input.DB(); + inValReads[BUTTON_SELECT] = input.DZ(); + inValReads[BUTTON_START] = input.DStart(); + if (input.PL()) + x20_wantsQuit = true; +} + +void CNESEmulator::Update() +{ + if (!EmulatorInst) + { + if (m_dvdReq && m_dvdReq->IsComplete()) + { + m_dvdReq.reset(); + InitializeEmulator(); + } + } + else + { + NesEmuMainLoop(); + } +} + +static const float NESAspect = VISIBLE_DOTS / float(VISIBLE_LINES); + +void CNESEmulator::Draw(const zeus::CColor& mulColor, bool filtering) +{ + if (!EmulatorInst) + return; + + float aspect = g_Viewport.x8_width / float(g_Viewport.xc_height); + float widthFac = NESAspect / aspect; + + Uniform uniform = {zeus::CMatrix4f{}, mulColor}; + uniform.m_matrix[0][0] = widthFac; + m_uniBuf->load(&uniform, sizeof(Uniform)); + + CGraphics::SetShaderDataBinding(m_shadBind); + CGraphics::DrawArray(0, 4); +} + +void CNESEmulator::LoadState(const u8* state) +{ + memmove(x39_loadState, state, 18); + x38_stateLoaded = true; +} + +} diff --git a/NESEmulator/CNESEmulator.hpp b/NESEmulator/CNESEmulator.hpp new file mode 100644 index 000000000..046226cab --- /dev/null +++ b/NESEmulator/CNESEmulator.hpp @@ -0,0 +1,81 @@ +#ifndef __URDE_CNESEMULATOR_HPP__ +#define __URDE_CNESEMULATOR_HPP__ + +#include "RetroTypes.hpp" +#include "zeus/CColor.hpp" +#include "boo/graphicsdev/IGraphicsDataFactory.hpp" +#include "boo/audiodev/IAudioVoice.hpp" +#include "zeus/CMatrix4f.hpp" + +namespace urde +{ +class CFinalInput; +class IDvdRequest; + +namespace MP1 +{ + +#define NUM_AUDIO_BUFFERS 10 + +class CNESEmulator : public boo::IAudioVoiceCallback +{ + static bool EmulatorConstructed; + + std::unique_ptr m_nesEmuPBuf; + std::shared_ptr m_dvdReq; + + struct Vert + { + zeus::CVector3f m_pos; + zeus::CVector2f m_uv; + }; + + struct Uniform + { + zeus::CMatrix4f m_matrix; + zeus::CColor m_color; + }; + + boo::ObjToken m_texture; + boo::ObjToken m_uniBuf; + boo::ObjToken m_vbo; + boo::ObjToken m_shadBind; + + std::unique_ptr m_audioBufBlock; + u8* m_audioBufs[NUM_AUDIO_BUFFERS]; + int m_headBuf = 0; + int m_tailBuf = 0; + int m_procBufs = NUM_AUDIO_BUFFERS; + size_t m_posInBuf = 0; + boo::ObjToken m_booVoice; + + bool x20_wantsQuit = false; + u8 x21_saveState[18]; + bool x34_wantsLoad = false; + bool x38_stateLoaded = false; + u8 x39_loadState[18]; + static void DecompressROM(u8* dataIn, u8* dataOut, u32 dataOutLen = 0x20000, u8 descrambleSeed = 0xe9, + u32 checkDataLen = 0x1FFFC, u32 checksumMagic = 0xA663); + void InitializeEmulator(); + void DeinitializeEmulator(); + void NesEmuMainLoop(); +public: + CNESEmulator(); + ~CNESEmulator(); + void ProcessUserInput(const CFinalInput& input, int); + void Update(); + void Draw(const zeus::CColor& mulColor, bool filtering); + void LoadState(const u8* state); + const u8* GetSaveState() const { return x21_saveState; } + bool WantsQuit() const { return x20_wantsQuit; } + bool WantsLoad() const { return x34_wantsLoad; } + + int audioUpdate(); + void preSupplyAudio(boo::IAudioVoice& voice, double dt) {} + size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data); +}; + +} +} + +#endif // __URDE_CNESEMULATOR_HPP__ diff --git a/NESEmulator/CNESShader.cpp b/NESEmulator/CNESShader.cpp new file mode 100644 index 000000000..03b0d1df6 --- /dev/null +++ b/NESEmulator/CNESShader.cpp @@ -0,0 +1,337 @@ +#include "CNESShader.hpp" +#include "Graphics/CGraphics.hpp" + +namespace urde::MP1 +{ + +boo::ObjToken CNESShader::g_Pipeline; +boo::ObjToken CNESShader::g_VtxFmt; + +static const char* VS_GLSL = +"#version 330\n" +BOO_GLSL_BINDING_HEAD +"layout(location=0) in vec4 posIn;\n" +"layout(location=1) in vec4 uvIn;\n" +"\n" +"UBINDING0 uniform TexuredQuadUniform\n" +"{\n" +" mat4 mtx;\n" +" vec4 color;\n" +"};\n" +"\n" +"struct VertToFrag\n" +"{\n" +" vec4 color;\n" +" vec2 uv;\n" +"};\n" +"\n" +"SBINDING(0) out VertToFrag vtf;\n" +"void main()\n" +"{\n" +" vtf.color = color;\n" +" vtf.uv = uvIn.xy;\n" +" gl_Position = mtx * vec4(posIn.xyz, 1.0);\n" +" gl_Position = FLIPFROMGL(gl_Position);\n" +"}\n"; + +static const char* FS_GLSL = +"#version 330\n" +BOO_GLSL_BINDING_HEAD +"struct VertToFrag\n" +"{\n" +" vec4 color;\n" +" vec2 uv;\n" +"};\n" +"\n" +"SBINDING(0) in VertToFrag vtf;\n" +"layout(location=0) out vec4 colorOut;\n" +"TBINDING0 uniform sampler2D tex;\n" +"void main()\n" +"{\n" +" colorOut = vtf.color * vec4(texture(tex, vtf.uv).bgr, 1.0);\n" +"}\n"; + +#if _WIN32 +static const char* VS_HLSL = +"struct VertData\n" +"{\n" +" float4 posIn : POSITION;\n" +" float4 uvIn : UV;\n" +"};\n" +"\n" +"cbuffer TexuredQuadUniform : register(b0)\n" +"{\n" +" float4x4 mat;\n" +" float4 color;\n" +"};\n" +"\n" +"struct VertToFrag\n" +"{\n" +" float4 position : SV_Position;\n" +" float4 color : COLOR;\n" +" float2 uv : UV;\n" +"};\n" +"\n" +"VertToFrag main(in VertData v)\n" +"{\n" +" VertToFrag vtf;\n" +" vtf.color = color;\n" +" vtf.uv = v.uvIn.xy;\n" +" vtf.position = mul(mat, float4(v.posIn.xyz, 1.0));\n" +" return vtf;\n" +"}\n"; + +static const char* FS_HLSL = +"struct VertToFrag\n" +"{\n" +" float4 position : SV_Position;\n" +" float4 color : COLOR;\n" +" float2 uv : UV;\n" +"};\n" +"\n" +"Texture2D tex : register(t0);\n" +"SamplerState samp : register(s3);\n" +"\n" +"float4 main(in VertToFrag vtf) : SV_Target0\n" +"{\n" +" return vtf.color * float4(tex.Sample(samp, vtf.uv).bgr, 1.0);\n" +"}\n"; +#endif + +#if BOO_HAS_METAL +static const char* VS_METAL = +"#include \n" +"using namespace metal;\n" +"struct VertData\n" +"{\n" +" float4 posIn [[ attribute(0) ]];\n" +" float4 uvIn [[ attribute(1) ]];\n" +"};\n" +"\n" +"struct TexuredQuadUniform\n" +"{\n" +" float4x4 mat;\n" +" float4 color;\n" +"};\n" +"\n" +"struct VertToFrag\n" +"{\n" +" float4 position [[ position ]];\n" +" float4 color;\n" +" float2 uv;\n" +"};\n" +"\n" +"vertex VertToFrag vmain(VertData v [[ stage_in ]], constant TexuredQuadUniform& tqu [[ buffer(2) ]])\n" +"{\n" +" VertToFrag vtf;\n" +" vtf.color = tqu.color;\n" +" vtf.uv = v.uvIn.xy;\n" +" vtf.position = tqu.mat * float4(v.posIn.xyz, 1.0);\n" +" return vtf;\n" +"}\n"; + + +static const char* FS_METAL = +"#include \n" +"using namespace metal;\n" +"struct VertToFrag\n" +"{\n" +" float4 position [[ position ]];\n" +" float4 color;\n" +" float2 uv;\n" +"};\n" +"\n" +"fragment float4 fmain(VertToFrag vtf [[ stage_in ]],\n" +" sampler clampSamp [[ sampler(3) ]],\n" +" texture2d tex [[ texture(0) ]])\n" +"{\n" +" return vtf.color * float4(tex.sample(clampSamp, vtf.uv).bgr, 1.0);\n" +"}\n"; +#endif + +#if BOO_HAS_GL +void CNESShader::Initialize(boo::GLDataFactory::Context& ctx) +{ + const char* texNames[] = {"tex"}; + const char* uniNames[] = {"TexuredQuadUniform"}; + g_Pipeline = ctx.newShaderPipeline(VS_GLSL, FS_GLSL, 1, texNames, 1, uniNames, boo::BlendFactor::SrcAlpha, + boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips, + boo::ZTest::None, false, true, false, boo::CullMode::None); +} + +boo::ObjToken CNESShader::BuildShaderDataBinding(boo::GLDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex) +{ + const boo::VertexElementDescriptor VtxVmt[] = + { + {vbo.get(), nullptr, boo::VertexSemantic::Position4}, + {vbo.get(), nullptr, boo::VertexSemantic::UV4} + }; + boo::ObjToken bufs[] = {uniBuf.get()}; + boo::PipelineStage stages[] = {boo::PipelineStage::Vertex}; + boo::ObjToken texs[] = {tex.get()}; + return ctx.newShaderDataBinding(g_Pipeline, + ctx.newVertexFormat(2, VtxVmt), vbo.get(), nullptr, nullptr, + 1, bufs, stages, nullptr, nullptr, 1, texs, nullptr, nullptr); +} +#endif + +#if _WIN32 +void CNESShader::Initialize(boo::ID3DDataFactory::Context& ctx) +{ + const boo::VertexElementDescriptor VtxVmt[] = + { + {nullptr, nullptr, boo::VertexSemantic::Position4}, + {nullptr, nullptr, boo::VertexSemantic::UV4} + }; + g_VtxFmt = ctx.newVertexFormat(2, VtxVmt); + g_Pipeline = ctx.newShaderPipeline(VS_HLSL, FS_HLSL, nullptr, nullptr, nullptr, + g_VtxFmt, boo::BlendFactor::SrcAlpha, + boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips, + boo::ZTest::None, false, true, false, boo::CullMode::None); +} + +boo::ObjToken CNESShader::BuildShaderDataBinding(boo::ID3DDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex) +{ + boo::ObjToken bufs[] = {filter.m_uniBuf.get()}; + boo::ObjToken texs[] = {filter.m_booTex.get()}; + return cctx.newShaderDataBinding(SelectPipeline(type, filter.m_gequal), s_VtxFmt, + filter.m_vbo.get(), nullptr, nullptr, 1, bufs, + nullptr, nullptr, nullptr, 1, texs, nullptr, nullptr); +} +#endif + +#if BOO_HAS_METAL +void CNESShader::Initialize(boo::MetalDataFactory::Context& ctx) +{ + const boo::VertexElementDescriptor VtxVmt[] = + { + {nullptr, nullptr, boo::VertexSemantic::Position4}, + {nullptr, nullptr, boo::VertexSemantic::UV4} + }; + g_VtxFmt = ctx.newVertexFormat(2, VtxVmt); + g_Pipeline = ctx.newShaderPipeline(VS_METAL, FS_METAL, nullptr, nullptr, + g_VtxFmt, boo::BlendFactor::SrcAlpha, + boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips, + boo::ZTest::None, false, true, false, boo::CullMode::None); +} + +boo::ObjToken CNESShader::BuildShaderDataBinding(boo::MetalDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex) +{ + boo::ObjToken bufs[] = {uniBuf.get()}; + boo::ObjToken texs[] = {tex.get()}; + return ctx.newShaderDataBinding(g_Pipeline, g_VtxFmt, + vbo.get(), nullptr, nullptr, 1, bufs, + nullptr, nullptr, nullptr, 1, texs, nullptr, nullptr); +} +#endif + +#if BOO_HAS_VULKAN +void CNESShader::Initialize(boo::VulkanDataFactory::Context& ctx) +{ + const boo::VertexElementDescriptor VtxVmt[] = + { + {nullptr, nullptr, boo::VertexSemantic::Position4}, + {nullptr, nullptr, boo::VertexSemantic::UV4} + }; + g_VtxFmt = ctx.newVertexFormat(2, VtxVmt); + g_Pipeline = ctx.newShaderPipeline(VS_GLSL, FS_GLSL, g_VtxFmt, boo::BlendFactor::SrcAlpha, + boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips, + boo::ZTest::None, false, true, false, boo::CullMode::None); +} + +boo::ObjToken CNESShader::BuildShaderDataBinding(boo::VulkanDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex) +{ + boo::ObjToken bufs[] = {filter.m_uniBuf.get()}; + boo::ObjToken texs[] = {filter.m_booTex.get()}; + return cctx.newShaderDataBinding(SelectPipeline(type, filter.m_gequal, + filter.m_booTex->type() == boo::TextureType::Render), s_VtxFmt, + filter.m_vbo.get(), nullptr, nullptr, 1, bufs, + nullptr, nullptr, nullptr, 1, texs, nullptr, nullptr); +} +#endif + +boo::ObjToken CNESShader::BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex) +{ + switch (ctx.platform()) + { +#if BOO_HAS_GL + case boo::IGraphicsDataFactory::Platform::OpenGL: + return BuildShaderDataBinding(static_cast(ctx), vbo, uniBuf, tex); +#endif +#if _WIN32 + case boo::IGraphicsDataFactory::Platform::D3D11: + case boo::IGraphicsDataFactory::Platform::D3D12: + return BuildShaderDataBinding(static_cast(ctx), vbo, uniBuf, tex); +#endif +#if BOO_HAS_METAL + case boo::IGraphicsDataFactory::Platform::Metal: + return BuildShaderDataBinding(static_cast(ctx), vbo, uniBuf, tex); +#endif +#if BOO_HAS_VULKAN + case boo::IGraphicsDataFactory::Platform::Vulkan: + return BuildShaderDataBinding(static_cast(ctx), vbo, uniBuf, tex); +#endif + default: + return {}; + } +} + +void CNESShader::Initialize() +{ + if (!CGraphics::g_BooFactory) + return; + + CGraphics::CommitResources( + [&](boo::IGraphicsDataFactory::Context& ctx) -> bool + { + switch (ctx.platform()) + { +#if BOO_HAS_GL + case boo::IGraphicsDataFactory::Platform::OpenGL: + Initialize(static_cast(ctx)); + break; +#endif +#if _WIN32 + case boo::IGraphicsDataFactory::Platform::D3D11: + case boo::IGraphicsDataFactory::Platform::D3D12: + Initialize(static_cast(ctx)); + break; +#endif +#if BOO_HAS_METAL + case boo::IGraphicsDataFactory::Platform::Metal: + Initialize(static_cast(ctx)); + break; +#endif +#if BOO_HAS_VULKAN + case boo::IGraphicsDataFactory::Platform::Vulkan: + Initialize(static_cast(ctx)); + break; +#endif + default: break; + } + return true; + }); +} + +void CNESShader::Shutdown() +{ + g_Pipeline.reset(); + g_VtxFmt.reset(); +} + +} diff --git a/NESEmulator/CNESShader.hpp b/NESEmulator/CNESShader.hpp new file mode 100644 index 000000000..c36cbfc8d --- /dev/null +++ b/NESEmulator/CNESShader.hpp @@ -0,0 +1,57 @@ +#ifndef URDE_CNESSHADER_HPP +#define URDE_CNESSHADER_HPP + +#include "boo/graphicsdev/GL.hpp" +#include "boo/graphicsdev/Metal.hpp" +#include "boo/graphicsdev/D3D.hpp" +#include "boo/graphicsdev/Vulkan.hpp" + +namespace urde::MP1 +{ + +class CNESShader +{ +public: +#if BOO_HAS_GL + static void Initialize(boo::GLDataFactory::Context& ctx); + static boo::ObjToken BuildShaderDataBinding(boo::GLDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex); +#endif +#if _WIN32 + static void Initialize(boo::ID3DDataFactory::Context& ctx); + static boo::ObjToken BuildShaderDataBinding(boo::ID3DDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex); +#endif +#if BOO_HAS_METAL + static void Initialize(boo::MetalDataFactory::Context& ctx); + static boo::ObjToken BuildShaderDataBinding(boo::MetalDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex); +#endif +#if BOO_HAS_VULKAN + static void Initialize(boo::VulkanDataFactory::Context& ctx); + static boo::ObjToken BuildShaderDataBinding(boo::VulkanDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex); +#endif + static void Initialize(); + static void Shutdown(); + + static boo::ObjToken BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx, + boo::ObjToken vbo, + boo::ObjToken uniBuf, + boo::ObjToken tex); + + static boo::ObjToken g_Pipeline; + static boo::ObjToken g_VtxFmt; +}; + +} + +#endif // URDE_CNESSHADER_HPP diff --git a/NESEmulator/fixNES b/NESEmulator/fixNES new file mode 160000 index 000000000..25527a513 --- /dev/null +++ b/NESEmulator/fixNES @@ -0,0 +1 @@ +Subproject commit 25527a513dc635fa87014970618fbfb7dfe3e5c6 diff --git a/NESEmulator/malloc.h b/NESEmulator/malloc.h new file mode 100644 index 000000000..ebd554502 --- /dev/null +++ b/NESEmulator/malloc.h @@ -0,0 +1,10 @@ +#ifndef URDE_NESEMULATOR_MALLOC_H +#define URDE_NESEMULATOR_MALLOC_H + +#ifdef __APPLE__ +#include +#else +#include <../include/malloc.h> +#endif + +#endif // URDE_NESEMULATOR_MALLOC_H diff --git a/Runtime/Audio/CStaticAudioPlayer.hpp b/Runtime/Audio/CStaticAudioPlayer.hpp index 9dac0160e..f7e9b8d43 100644 --- a/Runtime/Audio/CStaticAudioPlayer.hpp +++ b/Runtime/Audio/CStaticAudioPlayer.hpp @@ -76,7 +76,7 @@ public: void StartMixing() { - m_voice->start(); + //m_voice->start(); } void StopMixing() { diff --git a/Runtime/MP1/CFrontEndUI.cpp b/Runtime/MP1/CFrontEndUI.cpp index 3771731e6..3df877ee3 100644 --- a/Runtime/MP1/CFrontEndUI.cpp +++ b/Runtime/MP1/CFrontEndUI.cpp @@ -1223,9 +1223,9 @@ void CFrontEndUI::SFusionBonusFrame::DoAdvance(CGuiTableGroup* caller) } else if (g_GameState->SystemOptions().GetPlayerBeatFusion()) { - x8_action = EAction::None; + //x8_action = EAction::None; CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); - //x8_action = EAction::PlayNESMetroid; + x8_action = EAction::PlayNESMetroid; } else { @@ -1564,15 +1564,15 @@ bool CFrontEndUI::SNesEmulatorFrame::Update(float dt, CSaveGameScreen* saveUi) void CFrontEndUI::SNesEmulatorFrame::Draw(CSaveGameScreen* saveUi) const { zeus::CColor mulColor = zeus::CColor::skWhite; - bool doDraw = (saveUi && saveUi->GetUIType() != CSaveGameScreen::EUIType::SaveReady) ? false : true; + bool blackout = saveUi && saveUi->GetUIType() != CSaveGameScreen::EUIType::SaveReady; - if (doDraw) + if (blackout) mulColor = zeus::CColor::skBlack; else if (x8_quitScreen) mulColor = zeus::CColor{0.376470f, 0.376470f, 0.376470f, 1.f}; x4_nesEmu->Draw(mulColor, x15_enableFiltering); - if (!doDraw && x8_quitScreen) + if (!blackout && x8_quitScreen) x8_quitScreen->Draw(); if (x10_remTime >= 7.5f) @@ -2007,7 +2007,7 @@ CFrontEndUI::CFrontEndUI() m_touchBar = NewFrontEndUITouchBar(); m_touchBar->SetPhase(CFrontEndUITouchBar::EPhase::None); - x14_phase = EPhase::ExitFrontEnd; + //x14_phase = EPhase::ExitFrontEnd; } void CFrontEndUI::StartSlideShow(CArchitectureQueue& queue) @@ -2533,7 +2533,7 @@ static const float AudioFadeTimeB[] = CIOWin::EMessageReturn CFrontEndUI::Update(float dt, CArchitectureQueue& queue) { - if (xdc_saveUI && x50_curScreen >= EScreen::FileSelect) + if (xdc_saveUI && (x50_curScreen >= EScreen::FileSelect || true)) { switch (xdc_saveUI->Update(dt)) { @@ -2636,6 +2636,11 @@ CIOWin::EMessageReturn CFrontEndUI::Update(float dt, CArchitectureQueue& queue) } break; } + else + { + xec_emuFrme = std::make_unique(); + xdc_saveUI->ResetCardDriver(); + } if (xd2_deferSlideShow) { diff --git a/Runtime/MP1/CMakeLists.txt b/Runtime/MP1/CMakeLists.txt index cc3225257..869353120 100644 --- a/Runtime/MP1/CMakeLists.txt +++ b/Runtime/MP1/CMakeLists.txt @@ -11,7 +11,6 @@ set(MP1_SOURCES CTweaks.hpp CTweaks.cpp CInGameTweakManager.hpp CInGameTweakManager.cpp CGBASupport.hpp CGBASupport.cpp - CNESEmulator.hpp CNESEmulator.cpp CMainFlow.hpp CMainFlow.cpp CMFGame.hpp CMFGame.cpp CPlayMovie.hpp CPlayMovie.cpp diff --git a/Runtime/MP1/CNESEmulator.cpp b/Runtime/MP1/CNESEmulator.cpp deleted file mode 100644 index 8757a7e72..000000000 --- a/Runtime/MP1/CNESEmulator.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include "CNESEmulator.hpp" -#include "CGameState.hpp" -#include - -namespace urde::MP1 -{ - -void CNESEmulator::DescrambleROM(u8* dataIn, u8* dataOut, u32 dataOutLen, u8 descrambleInit, - u32 descramble2Len, u32 descramble2Magic) -{ - for (int i=0 ; i<256 ; ++i) - { - descrambleInit += dataIn[i]; - dataIn[i] = descrambleInit; - } - - for (int i=0 ; i<128 ; ++i) - std::swap(dataIn[255 - i], dataIn[i]); - - struct BitstreamState - { - u32 x0_remBits = 0; - u32 x4_lastByte; - u8* x8_ptr; - BitstreamState(u8* ptr) : x8_ptr(ptr) {} - } bState = {dataIn + 256}; - - u8* dataOutAlpha = dataOut; - while (dataOutLen != 0) - { - u32 r12 = 0; - - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - u32 r11 = r12 << 1; - if (bState.x4_lastByte & 0x80) - r11 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - - if (r11 == 1) - { - r12 = 0; - - for (int i=0 ; i<4 ; ++i) - { - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - r12 <<= 1; - if (bState.x4_lastByte & 0x80) - r12 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - r12 <<= 1; - if (bState.x4_lastByte & 0x80) - r12 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - } - - *dataOutAlpha++ = dataIn[r12 + 73]; - } - else - { - r12 = 0; - - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - r11 = r12 << 1; - if (bState.x4_lastByte & 0x80) - r11 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - - if (r11 == 1) - { - r12 = 0; - - for (int i=0 ; i<3 ; ++i) - { - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - r12 <<= 1; - if (bState.x4_lastByte & 0x80) - r12 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - r12 <<= 1; - if (bState.x4_lastByte & 0x80) - r12 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - } - - *dataOutAlpha++ = dataIn[r12 + 9]; - } - else - { - r12 = 0; - - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - r11 = r12 << 1; - if (bState.x4_lastByte & 0x80) - r11 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - - if (r11 == 1) - { - r12 = 0; - - for (int i=0 ; i<3 ; ++i) - { - if (bState.x0_remBits == 0) - { - bState.x4_lastByte = *bState.x8_ptr; - bState.x8_ptr += 1; - bState.x0_remBits = 8; - } - - r12 <<= 1; - if (bState.x4_lastByte & 0x80) - r12 |= 1; - - bState.x4_lastByte <<= 1; - bState.x0_remBits -= 1; - } - - *dataOutAlpha++ = dataIn[r12 + 1]; - } - else - { - *dataOutAlpha++ = dataIn[0]; - } - } - } - - --dataOutLen; - } - - u32 tmpWord = 0; - for (int i=0 ; i> 1) ^ descramble2Magic; - else - tmpWord = (tmpWord >> 1); - } - } - - dataOut[descramble2Len - 1] = (tmpWord >> 8) & 0xff; - dataOut[descramble2Len - 2] = tmpWord & 0xff; -} - -void CNESEmulator::ProcessUserInput(const CFinalInput& input, int) -{ - -} - -void CNESEmulator::Update() -{ - -} - -void CNESEmulator::Draw(const zeus::CColor& mulColor, bool filtering) -{ - -} - -void CNESEmulator::LoadState(const u8* state) -{ - memmove(x39_loadState, state, 18); - x38_stateLoaded = true; -} - -} diff --git a/Runtime/MP1/CNESEmulator.hpp b/Runtime/MP1/CNESEmulator.hpp deleted file mode 100644 index a77358591..000000000 --- a/Runtime/MP1/CNESEmulator.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef __URDE_CNESEMULATOR_HPP__ -#define __URDE_CNESEMULATOR_HPP__ - -#include "RetroTypes.hpp" -#include "zeus/CColor.hpp" - -namespace urde -{ -class CFinalInput; - -namespace MP1 -{ - -class CNESEmulator -{ - bool x20_wantsQuit = false; - u8 x21_saveState[18]; - bool x34_wantsLoad = false; - bool x38_stateLoaded = false; - u8 x39_loadState[18]; - static void DescrambleROM(u8* dataIn, u8* dataOut, u32 dataOutLen = 0x20000, u8 descrambleInit = 0xe9, - u32 descramble2Len = 0x1FFFC, u32 descramble2Magic = 0xA663); -public: - void ProcessUserInput(const CFinalInput& input, int); - void Update(); - void Draw(const zeus::CColor& mulColor, bool filtering); - void LoadState(const u8* state); - const u8* GetSaveState() const { return x21_saveState; } - bool WantsQuit() const { return x20_wantsQuit; } - bool WantsLoad() const { return x34_wantsLoad; } -}; - -} -} - -#endif // __URDE_CNESEMULATOR_HPP__ diff --git a/Runtime/MP1/MP1.cpp b/Runtime/MP1/MP1.cpp index aa258af9e..fd0f4999d 100644 --- a/Runtime/MP1/MP1.cpp +++ b/Runtime/MP1/MP1.cpp @@ -18,6 +18,7 @@ #include "Graphics/Shaders/CAABoxShader.hpp" #include "Graphics/Shaders/CWorldShadowShader.hpp" #include "Graphics/Shaders/CParticleSwooshShaders.hpp" +#include "NESEmulator/CNESShader.hpp" #include "Audio/CStreamAudioManager.hpp" #include "CGBASupport.hpp" @@ -297,6 +298,7 @@ CMain::BooSetter::BooSetter(boo::IGraphicsDataFactory* factory, TMultiBlendShader::Initialize(); TMultiBlendShader::Initialize(); TMultiBlendShader::Initialize(); + CNESShader::Initialize(); } void CMain::RegisterResourceTweaks() @@ -693,6 +695,7 @@ void CMain::Shutdown() TMultiBlendShader::Shutdown(); TMultiBlendShader::Shutdown(); CFluidPlaneShader::Shutdown(); + CNESShader::Shutdown(); CGraphics::ShutdownBoo(); ShutdownDiscord(); } diff --git a/Runtime/Weapon/CAuxWeapon.cpp b/Runtime/Weapon/CAuxWeapon.cpp index d5a6956c5..dcb2666f5 100644 --- a/Runtime/Weapon/CAuxWeapon.cpp +++ b/Runtime/Weapon/CAuxWeapon.cpp @@ -326,7 +326,7 @@ void CAuxWeapon::Fire(float dt, bool underwater, CPlayerState::EBeamId currentBe CWeapon::EProjectileAttrib attrib = CWeapon::EProjectileAttrib::None; if (chargeState == EChargeState::Charged) - attrib = CGameProjectile::GetBeamAttribType(type) | CWeapon::EProjectileAttrib::Unknown1; + attrib = CGameProjectile::GetBeamAttribType(type) | CWeapon::EProjectileAttrib::ComboShot; if (chargeState == EChargeState::Normal) { diff --git a/Runtime/Weapon/CGameProjectile.hpp b/Runtime/Weapon/CGameProjectile.hpp index 20b5119fa..ab9173dd3 100644 --- a/Runtime/Weapon/CGameProjectile.hpp +++ b/Runtime/Weapon/CGameProjectile.hpp @@ -43,9 +43,6 @@ public: void DeleteProjectileLight(CStateManager&); void CreateProjectileLight(std::string_view, const CLight&, CStateManager&); void Chase(float, CStateManager&); - EWeaponCollisionResponseTypes GetCollisionResponseType(const zeus::CVector3f&, const zeus::CVector3f&, - const CWeaponMode&, int) const - { return EWeaponCollisionResponseTypes::Projectile; } TUniqueId GetHomingTargetId() const { return x2c0_homingTargetId; } }; } diff --git a/Runtime/Weapon/CGunWeapon.cpp b/Runtime/Weapon/CGunWeapon.cpp index c4041136a..9b0c8b955 100644 --- a/Runtime/Weapon/CGunWeapon.cpp +++ b/Runtime/Weapon/CGunWeapon.cpp @@ -235,14 +235,12 @@ void CGunWeapon::Fire(bool underwater, float dt, EChargeState chargeState, const if (chargeState == EChargeState::Charged) attribs |= CWeapon::EProjectileAttrib::Charged; - /* CEnergyProjectile* proj = new CEnergyProjectile(true, x144_weapons[int(chargeState)], x1c0_weaponType, xf, x1c8_playerMaterial, dInfo, mgr.AllocateUniqueId(), kInvalidAreaId, x1c4_playerId, homingTarget, attribs, underwater, scale, {}, -1, false); mgr.AddObject(proj); proj->Think(dt, mgr); - */ if (chargeState == EChargeState::Charged) { diff --git a/Runtime/Weapon/CWeapon.cpp b/Runtime/Weapon/CWeapon.cpp index b919338d7..7527fb54f 100644 --- a/Runtime/Weapon/CWeapon.cpp +++ b/Runtime/Weapon/CWeapon.cpp @@ -1,5 +1,7 @@ #include "CWeapon.hpp" #include "World/CActorParameters.hpp" +#include "World/CScriptWater.hpp" +#include "CStateManager.hpp" #include "TCastTo.hpp" namespace urde @@ -10,7 +12,7 @@ CWeapon::CWeapon(TUniqueId uid, TAreaId aid, bool active, TUniqueId owner, EWeap const CMaterialList& mList, const CDamageInfo&, EProjectileAttrib attribs, CModelData&& mData) : CActor(uid, active, name, CEntityInfo(aid, CEntity::NullConnectionList), xf, std::move(mData), mList, CActorParameters::None(), kInvalidUniqueId) -, xf8_(filter) +, xf8_filter(filter) { } @@ -19,4 +21,93 @@ void CWeapon::Accept(urde::IVisitor& visitor) visitor.Visit(this); } +void CWeapon::Think(float dt, CStateManager& mgr) +{ + x148_curTime += dt; + if ((xe8_projectileAttribs & EProjectileAttrib::DamageFalloff) == EProjectileAttrib::DamageFalloff) + { + float damMul = std::max(0.f, 1.f - x148_curTime * x14c_damageFalloffSpeed); + x12c_curDamageInfo.SetDamage(x110_origDamageInfo.GetDamage() * damMul); + x12c_curDamageInfo.SetRadius(x110_origDamageInfo.GetRadius() * damMul); + x12c_curDamageInfo.SetKnockBackPower(x110_origDamageInfo.GetKnockBackPower() * damMul); + x12c_curDamageInfo.SetWeaponMode(x110_origDamageInfo.GetWeaponMode()); + x12c_curDamageInfo.SetNoImmunity(false); + } + else + { + x12c_curDamageInfo = x110_origDamageInfo; + } + CEntity::Think(dt, mgr); +} + +void CWeapon::Render(const CStateManager&) const +{ + // Empty +} + +EWeaponCollisionResponseTypes CWeapon::GetCollisionResponseType(const zeus::CVector3f&, const zeus::CVector3f&, + const CWeaponMode&, int) const +{ + return EWeaponCollisionResponseTypes::Projectile; +} + +void CWeapon::FluidFXThink(EFluidState state, CScriptWater& water, CStateManager& mgr) +{ + bool doRipple = true; + float mag = 0.f; + switch (xf0_weaponType) + { + case EWeaponType::Power: + mag = 0.1f; + break; + case EWeaponType::Ice: + mag = 0.3f; + break; + case EWeaponType::Wave: + mag = 0.1f; + break; + case EWeaponType::Plasma: + break; + case EWeaponType::Missile: + mag = 0.5f; + break; + case EWeaponType::Phazon: + mag = 0.1f; + break; + default: + doRipple = false; + break; + } + + if ((xe8_projectileAttribs & EProjectileAttrib::ComboShot) != EProjectileAttrib::None && + state != EFluidState::InFluid) + mag += 0.5f; + if ((xe8_projectileAttribs & EProjectileAttrib::Charged) != EProjectileAttrib::None) + mag += 0.25f; + if (mag > 1.f) + mag = 1.f; + + if (doRipple) + { + zeus::CVector3f pos = GetTranslation(); + pos.z = water.GetTriggerBoundsWR().max.z; + if ((xe8_projectileAttribs & EProjectileAttrib::ComboShot) != EProjectileAttrib::None) + { + if (!water.CanRippleAtPoint(pos)) + doRipple = false; + } + else if (state == EFluidState::InFluid) + { + doRipple = false; + } + + if (doRipple) + { + water.GetFluidPlane().AddRipple(mag, x8_uid, pos, water, mgr); + mgr.GetFluidPlaneManager()->CreateSplash(x8_uid, mgr, water, pos, mag, + state == EFluidState::EnteredFluid || state == EFluidState::LeftFluid); + } + } +} + } diff --git a/Runtime/Weapon/CWeapon.hpp b/Runtime/Weapon/CWeapon.hpp index 3d114efd5..a38029392 100644 --- a/Runtime/Weapon/CWeapon.hpp +++ b/Runtime/Weapon/CWeapon.hpp @@ -21,11 +21,12 @@ public: Wave = (1 << 4), Plasma = (1 << 5), Phazon = (1 << 6), - Unknown1 = (1 << 7), + ComboShot = (1 << 7), Bombs = (1 << 8), PowerBombs = (1 << 9), ArmCannon = (1 << 11), BigStrike = (1 << 12), + DamageFalloff = (1 << 13), StaticInterference = (1 << 14), KeepInCinematic = (1 << 17), }; @@ -35,12 +36,12 @@ private: TUniqueId xec_ownerId; EWeaponType xf0_weaponType; u32 xf4_; - CMaterialFilter xf8_; + CMaterialFilter xf8_filter; u32 x10c_; - CDamageInfo x110_; - CDamageInfo x12c_; - float x148_; - float x14c_; + CDamageInfo x110_origDamageInfo; + CDamageInfo x12c_curDamageInfo; + float x148_curTime; + float x14c_damageFalloffSpeed; float x150_damageDuration; float x154_interferenceDuration; public: @@ -49,27 +50,25 @@ public: const CMaterialList& mList, const CDamageInfo&, EProjectileAttrib attribs, CModelData&& mData); virtual void Accept(IVisitor &visitor); - bool HasAttrib(EProjectileAttrib) const; + bool HasAttrib(EProjectileAttrib attrib) const + { return (int(xe8_projectileAttribs) & int(attrib)) == int(attrib); } EProjectileAttrib GetAttribField() const { return xe8_projectileAttribs; } - const CMaterialFilter& GetFilter() const; - void SetFilter(const CMaterialFilter&); + const CMaterialFilter& GetFilter() const { return xf8_filter; } + void SetFilter(const CMaterialFilter& filter) { xf8_filter = filter; } TUniqueId GetOwnerId() const { return xec_ownerId; } void SetOwnerId(TUniqueId oid) { xec_ownerId = oid; } - EWeaponType GetType() const; - const CDamageInfo& GetDamageInfo() const; - CDamageInfo& DamageInfo(); - void SetDamageInfo(const CDamageInfo&); + EWeaponType GetType() const { return xf0_weaponType; } + const CDamageInfo& GetDamageInfo() const { return x12c_curDamageInfo; } + CDamageInfo& DamageInfo() { return x12c_curDamageInfo; } + void SetDamageInfo(const CDamageInfo& dInfo) { x12c_curDamageInfo = dInfo; } float GetDamageDuration() const { return x150_damageDuration; } float GetInterferenceDuration() const { return x154_interferenceDuration; } - void Think(float, CStateManager &) {} - void Render(const CStateManager&) const {} + void Think(float, CStateManager &); + void Render(const CStateManager&) const; EWeaponCollisionResponseTypes GetCollisionResponseType(const zeus::CVector3f&, const zeus::CVector3f&, - const CWeaponMode&, int) const - { - return EWeaponCollisionResponseTypes::Default; - } - void FluidFXThink(EFluidState, CScriptWater&, CStateManager&) {} + const CWeaponMode&, int) const; + void FluidFXThink(EFluidState state, CScriptWater& water, CStateManager& mgr); }; ENABLE_BITWISE_ENUM(CWeapon::EProjectileAttrib) } diff --git a/Runtime/World/CDamageInfo.hpp b/Runtime/World/CDamageInfo.hpp index 4bb78fec7..020200ef3 100644 --- a/Runtime/World/CDamageInfo.hpp +++ b/Runtime/World/CDamageInfo.hpp @@ -43,14 +43,18 @@ public: CDamageInfo& operator=(const DataSpec::SShotParam& other); const CWeaponMode& GetWeaponMode() const { return x0_weaponMode; } + void SetWeaponMode(const CWeaponMode& mode) { x0_weaponMode = mode; } float GetRadius() const { return x10_radius; } void SetRadius(float r) { x10_radius = r; } float GetKnockBackPower() const { return x14_knockback; } + void SetKnockBackPower(float k) { x14_knockback = k; } float GetDamage() const { return x8_damage; } + void SetDamage(float d) { x8_damage = d; } float GetDamage(const CDamageVulnerability& dVuln) const; float GetRadiusDamage() const { return xc_radiusDamage; } float GetRadiusDamage(const CDamageVulnerability& dVuln) const; bool NoImmunity() const { return x18_noImmunity; } + void SetNoImmunity(bool b) { x18_noImmunity = b; } void MultiplyDamage(float m) { x8_damage *= m; diff --git a/Runtime/World/CFluidPlaneManager.cpp b/Runtime/World/CFluidPlaneManager.cpp index 48eeea9ac..900e156d0 100644 --- a/Runtime/World/CFluidPlaneManager.cpp +++ b/Runtime/World/CFluidPlaneManager.cpp @@ -1,4 +1,7 @@ #include "CFluidPlaneManager.hpp" +#include "CScriptWater.hpp" +#include "CExplosion.hpp" +#include "CStateManager.hpp" namespace urde { @@ -14,7 +17,11 @@ CFluidPlaneManager::CFluidPlaneManager() void CFluidPlaneManager::CFluidProfile::Clear() { - + x0_ = 0.f; + x4_ = 0.f; + x8_ = 0.f; + xc_ = 0.f; + x10_ = 0.f; } void CFluidPlaneManager::StartFrame(bool b) @@ -25,7 +32,14 @@ void CFluidPlaneManager::StartFrame(bool b) void CFluidPlaneManager::Update(float dt) { - + x11c_uvT += dt; + x0_rippleManager.Update(dt); + for (CSplashRecord& record : x18_splashes) + { + record.SetTime(record.GetTime() + dt); + if (record.GetTime() > 9999.f) + record.SetTime(9999.f); + } } float CFluidPlaneManager::GetLastRippleDeltaTime(TUniqueId rippler) const @@ -35,13 +49,47 @@ float CFluidPlaneManager::GetLastRippleDeltaTime(TUniqueId rippler) const float CFluidPlaneManager::GetLastSplashDeltaTime(TUniqueId splasher) const { - return 0.f; + float newestTime = 9999.f; + for (const CSplashRecord& record : x18_splashes) + if (record.GetUniqueId() == splasher && newestTime > record.GetTime()) + newestTime = record.GetTime(); + return newestTime; } void CFluidPlaneManager::CreateSplash(TUniqueId splasher, CStateManager& mgr, const CScriptWater& water, - const zeus::CVector3f& pos, float factor, bool) + const zeus::CVector3f& pos, float factor, bool sfx) { - + if (water.CanRippleAtPoint(pos)) + { + float oldestTime = 0.f; + CSplashRecord* oldestRecord = nullptr; + for (CSplashRecord& record : x18_splashes) + { + if (record.GetTime() > oldestTime) + { + oldestRecord = &record; + oldestTime = record.GetTime(); + } + } + if (oldestRecord) + *oldestRecord = CSplashRecord(0.f, splasher); + else + x18_splashes.emplace_back(0.f, splasher); + float splashScale = water.GetSplashEffectScale(factor); + if (water.GetSplashEffect(factor)) + { + CExplosion* expl = new CExplosion(*water.GetSplashEffect(factor), mgr.AllocateUniqueId(), true, + CEntityInfo(water.GetAreaIdAlways(), CEntity::NullConnectionList), + "Splash", zeus::CTransform(zeus::CMatrix3f::skIdentityMatrix3f, pos), + 1, zeus::CVector3f{splashScale}, water.GetSplashColor()); + mgr.AddObject(expl); + } + if (sfx) + { + CSfxManager::AddEmitter(water.GetSplashSound(factor), pos, zeus::CVector3f::skUp, + true, false, 0x7f, kInvalidAreaId); + } + } } u8 CFluidPlaneManager::RippleValues[64][64] = {}; diff --git a/Runtime/World/CFluidPlaneManager.hpp b/Runtime/World/CFluidPlaneManager.hpp index 2561062d2..34c51cac8 100644 --- a/Runtime/World/CFluidPlaneManager.hpp +++ b/Runtime/World/CFluidPlaneManager.hpp @@ -13,11 +13,14 @@ class CFluidPlaneManager { class CSplashRecord { + float x0_time = 0.f; + TUniqueId x4_id; public: - CSplashRecord(TUniqueId id); - void SetTime(float t); - float GetTime() const; - TUniqueId GetUniqueId() const; + CSplashRecord(float time, TUniqueId id) : x0_time(time), x4_id(id) {} + void SetTime(float t) { x0_time = t; } + float GetTime() const { return x0_time; } + void SetUniqueId(TUniqueId id) { x4_id = id; } + TUniqueId GetUniqueId() const { return x4_id; } }; CRippleManager x0_rippleManager; @@ -28,6 +31,11 @@ class CFluidPlaneManager class CFluidProfile { + float x0_ = 0.f; + float x4_ = 0.f; + float x8_ = 0.f; + float xc_ = 0.f; + float x10_ = 0.f; public: void Clear(); }; @@ -46,7 +54,7 @@ public: float GetLastRippleDeltaTime(TUniqueId rippler) const; float GetLastSplashDeltaTime(TUniqueId splasher) const; void CreateSplash(TUniqueId splasher, CStateManager& mgr, const CScriptWater& water, - const zeus::CVector3f& pos, float factor, bool); + const zeus::CVector3f& pos, float factor, bool sfx); rstl::reserved_vector& SplashRecords() { return x18_splashes; } const CRippleManager& GetRippleManager() const { return x0_rippleManager; } CRippleManager& RippleManager() { return x0_rippleManager; } diff --git a/Runtime/World/CRippleManager.cpp b/Runtime/World/CRippleManager.cpp index 3826e151f..4a5025586 100644 --- a/Runtime/World/CRippleManager.cpp +++ b/Runtime/World/CRippleManager.cpp @@ -23,7 +23,12 @@ void CRippleManager::SetTime(float) void CRippleManager::Update(float dt) { - + for (CRipple& ripple : x4_ripples) + { + ripple.SetTime(ripple.GetTime() + dt); + if (ripple.GetTime() > 9999.f) + ripple.SetTime(9999.f); + } } float CRippleManager::GetLastRippleDeltaTime(TUniqueId rippler) const diff --git a/Runtime/World/CScriptWater.cpp b/Runtime/World/CScriptWater.cpp index 13363af09..07ba8194e 100644 --- a/Runtime/World/CScriptWater.cpp +++ b/Runtime/World/CScriptWater.cpp @@ -552,14 +552,14 @@ EWeaponCollisionResponseTypes CScriptWater::GetCollisionResponseType(const zeus: return EWeaponCollisionResponseTypes::Water; } -s16 CScriptWater::GetSplashSound(float dt) const +u16 CScriptWater::GetSplashSound(float mag) const { - return x298_splashSounds[GetSplashIndex(dt)]; + return x298_splashSounds[GetSplashIndex(mag)]; } -const std::experimental::optional>& CScriptWater::GetSplashEffect(float dt) const +const std::experimental::optional>& CScriptWater::GetSplashEffect(float mag) const { - return x264_splashEffects[GetSplashIndex(dt)]; + return x264_splashEffects[GetSplashIndex(mag)]; } float CScriptWater::GetSplashEffectScale(float dt) const @@ -572,9 +572,9 @@ float CScriptWater::GetSplashEffectScale(float dt) const return ((1.f - s) * (s * kSplashScales[idx * 2])) + kSplashScales[idx]; } -u32 CScriptWater::GetSplashIndex(float dt) const +u32 CScriptWater::GetSplashIndex(float mag) const { - auto idx = u32(dt * 3.f); + auto idx = u32(mag * 3.f); return (idx < 3 ? idx : idx - 1); } diff --git a/Runtime/World/CScriptWater.hpp b/Runtime/World/CScriptWater.hpp index 5d46d3252..ad280b23e 100644 --- a/Runtime/World/CScriptWater.hpp +++ b/Runtime/World/CScriptWater.hpp @@ -106,7 +106,7 @@ public: EWeaponCollisionResponseTypes GetCollisionResponseType(const zeus::CVector3f&, const zeus::CVector3f&, const CWeaponMode&, int) const; - s16 GetSplashSound(float) const; + u16 GetSplashSound(float) const; const std::experimental::optional>& GetSplashEffect(float) const; float GetSplashEffectScale(float) const; u32 GetSplashIndex(float) const; diff --git a/hecl b/hecl index 2490ec82a..3454e81f9 160000 --- a/hecl +++ b/hecl @@ -1 +1 @@ -Subproject commit 2490ec82ae2ee1777852fa5322fdf244913cbc7b +Subproject commit 3454e81f97ae951b85cd4a9e18bb800f6123163e