From 256a809750c5492e5d858c89cc6ea20ffa5e23f3 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 11:52:05 +0100 Subject: [PATCH 1/4] feat/bttr-cwd --- .../Platform/Common/Consoles_App.cpp | 30 +++++++++++++++++++ Minecraft.World/IO/Files/File.cpp | 23 ++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/Minecraft.Client/Platform/Common/Consoles_App.cpp b/Minecraft.Client/Platform/Common/Consoles_App.cpp index f85d56632..5eb204c95 100644 --- a/Minecraft.Client/Platform/Common/Consoles_App.cpp +++ b/Minecraft.Client/Platform/Common/Consoles_App.cpp @@ -52,6 +52,10 @@ #include "../Minecraft.Client/Utils/ArchiveFile.h" #endif #include "../Minecraft.Client/Minecraft.h" +#if defined(__linux__) +#include +#include +#endif #ifdef _XBOX #include "../Minecraft.Client/Platform/Xbox/GameConfig/Minecraft.spa.h" #include "../Minecraft.Client/Platform/Xbox/Network/NetworkPlayerXbox.h" @@ -4600,7 +4604,33 @@ void CMinecraftApp::loadMediaArchive() { #endif if (!mediapath.empty()) { + // (check file.cpp) + // try to load the archive relative to the executable + // directory first. + // If that fails, fall back to the current working + // directory (original behavior) + // if everything fails, may god help you +#if defined(__linux__) + char exePathBuf[PATH_MAX]; + ssize_t exeLen = readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); + if (exeLen != -1) { + exePathBuf[exeLen] = '\0'; + std::string exePathStr(exePathBuf); + size_t pos = exePathStr.find_last_of('/'); + std::string exeDir = (pos == std::string::npos) ? std::string(".") : exePathStr.substr(0, pos); + std::wstring exeDirW = convStringToWstring(exeDir.c_str()); + std::wstring candidate = exeDirW + File::pathSeparator + mediapath; + if (File(candidate).exists()) { + m_mediaArchive = new ArchiveFile(File(candidate)); + } else { + m_mediaArchive = new ArchiveFile(File(mediapath)); + } + } else { + m_mediaArchive = new ArchiveFile(File(mediapath)); + } +#else m_mediaArchive = new ArchiveFile(File(mediapath)); +#endif } #if 0 std::string path = "Common\\media.arc"; diff --git a/Minecraft.World/IO/Files/File.cpp b/Minecraft.World/IO/Files/File.cpp index fb09c1a4f..e5bff5c98 100644 --- a/Minecraft.World/IO/Files/File.cpp +++ b/Minecraft.World/IO/Files/File.cpp @@ -69,6 +69,29 @@ File::File(const std::wstring& pathname) //: parent( NULL ) else m_abstractPathName = pathname; +#if defined(__linux__) + // If this is a relative path and it doesn't exist in the CWD, try to + // resolve it relative to the executable directory + if (!m_abstractPathName.empty() && m_abstractPathName[0] != L'/') { + const char* native = wstringtofilename(m_abstractPathName); + if (access(native, F_OK) == -1) { + char exePathBuf[PATH_MAX]; + ssize_t exeLen = readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); + if (exeLen != -1) { + exePathBuf[exeLen] = '\0'; + std::string exePathStr(exePathBuf); + size_t pos = exePathStr.find_last_of('/'); + std::string exeDir = (pos == std::string::npos) ? std::string(".") : exePathStr.substr(0, pos); + std::wstring exeDirW = convStringToWstring(exeDir); + std::wstring candidate = exeDirW + pathSeparator + m_abstractPathName; + const char* candNative = wstringtofilename(candidate); + if (access(candNative, F_OK) != -1) { + m_abstractPathName = candidate; + } + } + } + } +#endif #ifdef _WINDOWS64 std::string path = wstringtofilename(m_abstractPathName); std::string finalPath = StorageManager.GetMountedPath(path.c_str()); From ca84ac0512f4a9ddcac04bae12455f41171498fd Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 18:07:38 +0100 Subject: [PATCH 2/4] okay managed to do it, anyways hacky code land :3 mainly rewrote a bit of the soundengine, made files now load without being mean and uh yeah it works --- .../Platform/Common/Audio/SoundEngine.cpp | 660 +++++++----------- Minecraft.Client/Textures/BufferedImage.cpp | 272 ++++---- Minecraft.World/IO/Files/File.cpp | 41 +- meson.build | 2 +- 4 files changed, 427 insertions(+), 548 deletions(-) diff --git a/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp b/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp index d9b52d283..fa53701b9 100644 --- a/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp +++ b/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp @@ -82,8 +82,8 @@ const char* SoundEngine::m_szStreamFileA[eStream_Max] = {"calm1", "ward", "where_are_we_now"}; #ifdef __linux__ -char SoundEngine::m_szSoundPath[] = {"Sound/"}; -char SoundEngine::m_szMusicPath[] = {"music/"}; +char SoundEngine::m_szSoundPath[] = {"Common/Sound/"}; +char SoundEngine::m_szMusicPath[] = {"Common/"}; char SoundEngine::m_szRedistName[] = {"redist64"}; #endif @@ -190,168 +190,144 @@ void SoundEngine::init(Options* pOptions) { void SoundEngine::destroy() { ma_engine_uninit(&m_engine); } void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch) { - char szSoundName[256] = "Sound/Minecraft/"; + if (iSound == -1) return; - if (iSound == -1) { - app.DebugPrintf(6, "PlaySound with sound of -1 !!!!!!!!!!!!!!!\n"); - return; + char szIdentifier[256]; + wcstombs(szIdentifier, wchSoundNames[iSound], 255); + + // dot to folder structure (example step.grass -> step/grass) + for (int i = 0; szIdentifier[i]; i++) { + if (szIdentifier[i] == '.') szIdentifier[i] = '/'; } - wcstombs(szSoundName + 16, wchSoundNames[iSound], - sizeof(szSoundName) - 16 - 1); - szSoundName[sizeof(szSoundName) - 1] = '\0'; - - char finalPath[256]; + // YES I KNOW SOUNDNAMES.CPP EXISTS. const char* extensions[] = {".ogg", ".wav", ".mp3"}; - size_t extCount = sizeof(extensions) / sizeof(extensions[0]); + const char* roots[] = {"Sound/Minecraft/", + "build/Minecraft.Client/Sound/Minecraft/", + "Common/Sound/Minecraft/", + "Common/res/TitleUpdate/res/Sound/Minecraft/"}; + + char finalPath[512] = {0}; bool found = false; + // search for variants (grass1, grass2, etc.) + // this is hacky + for (const char* root : roots) { + for (const char* ext : extensions) { + int maxVariant = 0; + for (int i = 1; i <= 16; i++) { + char tryPath[512]; + snprintf(tryPath, sizeof(tryPath), "%s%s%d%s", root, + szIdentifier, i, ext); + if (access(tryPath, F_OK) != -1) + maxVariant = i; + else + break; + } - for (size_t extIdx = 0; extIdx < extCount; extIdx++) { - char basePlusExt[256]; - sprintf_s(basePlusExt, "%s%s", szSoundName, extensions[extIdx]); - - DWORD attr = GetFileAttributesA(basePlusExt); - if (attr != INVALID_FILE_ATTRIBUTES && - !(attr & FILE_ATTRIBUTE_DIRECTORY)) { - sprintf_s(finalPath, "%s", basePlusExt); - found = true; - break; - } - } - if (!found) { - int count = 0; - - for (size_t extIdx = 0; extIdx < extCount; extIdx++) { - for (size_t i = 1; i < 32; i++) { - char numberedPath[256]; - sprintf_s(numberedPath, "%s%d%s", szSoundName, i, - extensions[extIdx]); - - DWORD attr = GetFileAttributesA(numberedPath); - if (attr != INVALID_FILE_ATTRIBUTES && - !(attr & FILE_ATTRIBUTE_DIRECTORY)) { - count = i; - } + if (maxVariant > 0) { + int chosen = (rand() % maxVariant) + 1; + snprintf(finalPath, sizeof(finalPath), "%s%s%d%s", root, + szIdentifier, chosen, ext); + found = true; + break; } } - - if (count > 0) { - int chosen = (rand() % count) + 1; - for (size_t extIdx = 0; extIdx < extCount; extIdx++) { - char numberedPath[256]; - sprintf_s(numberedPath, "%s%d%s", szSoundName, chosen, - extensions[extIdx]); - - DWORD attr = GetFileAttributesA(numberedPath); - if (attr != INVALID_FILE_ATTRIBUTES && - !(attr & FILE_ATTRIBUTE_DIRECTORY)) { - sprintf_s(finalPath, "%s", numberedPath); + if (found) break; + } + if (!found) { + for (const char* root : roots) { + for (const char* ext : extensions) { + char tryPath[512]; + snprintf(tryPath, sizeof(tryPath), "%s%s%s", root, szIdentifier, + ext); + if (access(tryPath, F_OK) != -1) { + strncpy(finalPath, tryPath, 511); found = true; break; } } - if (!found) { - sprintf_s(finalPath, "%s%d.ogg", szSoundName, chosen); - } + if (found) break; } } + if (!found) return; + MiniAudioSound* s = new MiniAudioSound(); memset(&s->info, 0, sizeof(AUDIO_INFO)); - s->info.x = x; s->info.y = y; s->info.z = z; - s->info.volume = volume; s->info.pitch = pitch; s->info.bIs3D = true; - s->info.bUseSoundsPitchVal = false; - s->info.iSound = iSound + eSFX_MAX; if (ma_sound_init_from_file(&m_engine, finalPath, MA_SOUND_FLAG_ASYNC, - nullptr, nullptr, &s->sound) != MA_SUCCESS) { - app.DebugPrintf("Failed to load sound ID : %i from %S\n", iSound, - wchSoundNames[iSound]); + nullptr, nullptr, &s->sound) == MA_SUCCESS) { + ma_sound_set_spatialization_enabled(&s->sound, MA_TRUE); + ma_sound_set_min_distance(&s->sound, 2.0f); + ma_sound_set_max_distance(&s->sound, 48.0f); + ma_sound_set_volume(&s->sound, volume * m_MasterEffectsVolume); + ma_sound_set_pitch(&s->sound, pitch); + ma_sound_set_position(&s->sound, x, y, z); + ma_sound_start(&s->sound); + m_activeSounds.push_back(s); + } else { delete s; - return; } - - ma_sound_set_spatialization_enabled(&s->sound, MA_TRUE); - ma_sound_set_min_distance(&s->sound, SFX_3D_MIN_DISTANCE); - ma_sound_set_max_distance(&s->sound, SFX_3D_MAX_DISTANCE); - ma_sound_set_rolloff(&s->sound, SFX_3D_ROLLOFF); - - float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER; - if (finalVolume > SFX_MAX_GAIN) finalVolume = SFX_MAX_GAIN; - - ma_sound_set_volume(&s->sound, finalVolume); - ma_sound_set_pitch(&s->sound, pitch); - ma_sound_set_position(&s->sound, x, y, z); - - ma_sound_start(&s->sound); - - m_activeSounds.push_back(s); } void SoundEngine::playUI(int iSound, float volume, float pitch) { - char szSoundName[256]; - wstring name; - + char szIdentifier[256]; if (iSound >= eSFX_MAX) { - strcpy(szSoundName, "Sound/Minecraft/"); - name = wchSoundNames[iSound]; + wcstombs(szIdentifier, wchSoundNames[iSound], 255); } else { - strcpy(szSoundName, "Sound/Minecraft/UI/"); - name = wchUISoundNames[iSound]; + wcstombs(szIdentifier, wchUISoundNames[iSound], 255); } - wcstombs(szSoundName + strlen(szSoundName), name.c_str(), - sizeof(szSoundName) - strlen(szSoundName) - 1); - char finalPath[256]; + + for (int i = 0; szIdentifier[i]; i++) { + if (szIdentifier[i] == '.') szIdentifier[i] = '/'; + } + // ui sfx also WHO WHO THAT EWWW const char* extensions[] = {".ogg", ".wav", ".mp3"}; - size_t extCount = sizeof(extensions) / sizeof(extensions[0]); + const char* roots[] = {"Sound/Minecraft/UI/", "Sound/Minecraft/", + "build/Minecraft.Client/Sound/Minecraft/UI/", + "build/Minecraft.Client/Sound/Minecraft/", + "Common/Sound/Minecraft/UI/"}; + + char finalPath[512] = {0}; bool found = false; - for (size_t extIdx = 0; extIdx < extCount; extIdx++) { - char basePlusExt[256]; - sprintf_s(basePlusExt, "%s%s", szSoundName, extensions[extIdx]); - - DWORD attr = GetFileAttributesA(basePlusExt); - if (attr != INVALID_FILE_ATTRIBUTES && - !(attr & FILE_ATTRIBUTE_DIRECTORY)) { - sprintf_s(finalPath, "%s", basePlusExt); - found = true; - break; + for (const char* root : roots) { + for (const char* ext : extensions) { + char tryPath[512]; + snprintf(tryPath, sizeof(tryPath), "%s%s%s", root, szIdentifier, + ext); + if (access(tryPath, F_OK) != -1) { + strncpy(finalPath, tryPath, 511); + found = true; + break; + } } + if (found) break; } + if (!found) return; + MiniAudioSound* s = new MiniAudioSound(); memset(&s->info, 0, sizeof(AUDIO_INFO)); - s->info.volume = volume; s->info.pitch = pitch; s->info.bIs3D = false; - s->info.bUseSoundsPitchVal = true; if (ma_sound_init_from_file(&m_engine, finalPath, MA_SOUND_FLAG_ASYNC, - nullptr, nullptr, &s->sound) != MA_SUCCESS) { + nullptr, nullptr, &s->sound) == MA_SUCCESS) { + ma_sound_set_spatialization_enabled(&s->sound, MA_FALSE); + ma_sound_set_volume(&s->sound, volume * m_MasterEffectsVolume); + ma_sound_set_pitch(&s->sound, pitch); + ma_sound_start(&s->sound); + m_activeSounds.push_back(s); + } else { delete s; - app.DebugPrintf("ma_sound_init_from_file failed: %s\n", finalPath); - return; } - - ma_sound_set_spatialization_enabled(&s->sound, MA_FALSE); - - float finalVolume = volume * m_MasterEffectsVolume; - if (finalVolume > 1.0f) finalVolume = 1.0f; - printf("UI Sound volume set to %f\nEffects volume: %f\n", finalVolume, - m_MasterEffectsVolume); - - ma_sound_set_volume(&s->sound, finalVolume); - ma_sound_set_pitch(&s->sound, pitch); - - ma_sound_start(&s->sound); - - m_activeSounds.push_back(s); } int SoundEngine::getMusicID(int iDomain) { @@ -507,142 +483,102 @@ void SoundEngine::playMusicTick() { switch (m_StreamState) { case eMusicStreamState_Idle: - - // start a stream playing if (m_iMusicDelay > 0) { m_iMusicDelay--; return; } if (m_musicStreamActive) { - app.DebugPrintf( - "WARNING: m_musicStreamActive already true in Idle state, " - "resetting to Playing\n"); m_StreamState = eMusicStreamState_Playing; return; } if (m_musicID != -1) { - // start playing it + bool isCD = (m_musicID >= m_iStream_CD_1); + const char* folder = isCD ? "cds/" : "music/"; + const char* trackName = m_szStreamFileA[m_musicID]; + const char* extensions[] = {".ogg", ".mp3", ".wav"}; - strcpy((char*)m_szStreamName, m_szMusicPath); - // are we using a mash-up pack? - // if(pMinecraft && !pMinecraft->skins->isUsingDefaultSkin() && - // pMinecraft->skins->getSelected()->hasAudio()) + bool found = false; + m_szStreamName[0] = '\0'; + + // DLC Mashup pack check if (Minecraft::GetInstance() ->skins->getSelected() ->hasAudio()) { - // It's a mash-up - need to use the DLC path for the music TexturePack* pTexPack = Minecraft::GetInstance()->skins->getSelected(); - DLCTexturePack* pDLCTexPack = (DLCTexturePack*)pTexPack; - DLCPack* pack = pDLCTexPack->getDLCInfoParentPack(); + DLCPack* pack = + ((DLCTexturePack*)pTexPack)->getDLCInfoParentPack(); DLCAudioFile* dlcAudioFile = (DLCAudioFile*)pack->getFile( DLCManager::e_DLCType_Audio, 0); - app.DebugPrintf("Mashup pack \n"); - - // build the name - - // if the music ID is beyond the end of the texture pack - // music files, then it's a CD - if (m_musicID < m_iStream_CD_1) { - SetIsPlayingStreamingGameMusic(true); - SetIsPlayingStreamingCDMusic(false); + if (!isCD) { m_MusicType = eMusicType_Game; m_StreamingAudioInfo.bIs3D = false; wstring& wstrSoundName = dlcAudioFile->GetSoundName(m_musicID); - char szName[255]; wcstombs(szName, wstrSoundName.c_str(), 255); - string strFile = + std::string strFile = "TPACK:\\Data\\" + string(szName) + ".wav"; - std::string mountedPath = StorageManager.GetMountedPath(strFile); strcpy(m_szStreamName, mountedPath.c_str()); - } else { - SetIsPlayingStreamingGameMusic(false); - SetIsPlayingStreamingCDMusic(true); - m_MusicType = eMusicType_CD; - m_StreamingAudioInfo.bIs3D = true; - // Need to adjust to index into the cds in the game's - // m_szStreamFileA - strcat((char*)m_szStreamName, "cds/"); - strcat((char*)m_szStreamName, - m_szStreamFileA[m_musicID - m_iStream_CD_1 + - eStream_CD_1]); - strcat((char*)m_szStreamName, ".wav"); + if (access(m_szStreamName, F_OK) != -1) found = true; } - } else { - if (m_musicID < m_iStream_CD_1) { - SetIsPlayingStreamingGameMusic(true); - SetIsPlayingStreamingCDMusic(false); - m_MusicType = eMusicType_Game; - m_StreamingAudioInfo.bIs3D = false; - // build the name - strcat((char*)m_szStreamName, "music/"); - } else { - SetIsPlayingStreamingGameMusic(false); - SetIsPlayingStreamingCDMusic(true); - m_MusicType = eMusicType_CD; - m_StreamingAudioInfo.bIs3D = true; - // build the name - strcat((char*)m_szStreamName, "cds/"); - } - strcat((char*)m_szStreamName, m_szStreamFileA[m_musicID]); - strcat((char*)m_szStreamName, ".wav"); } - FILE* pFile = nullptr; - pFile = fopen(reinterpret_cast(m_szStreamName), "rb"); - if (pFile) { - fclose(pFile); - } else { - const char* extensions[] = {".ogg", ".mp3", ".wav"}; - size_t extCount = - sizeof(extensions) / sizeof(extensions[0]); - bool found = false; + // we're doing this again, daring aren't we + if (!found) { + const char* roots[] = { + "build/Minecraft.Client/Common/music/", + "build/Minecraft.Client/music/", "Common/music/", + "music/", "./"}; - char* dotPos = - strrchr(reinterpret_cast(m_szStreamName), '.'); - if (dotPos != nullptr && - (dotPos - reinterpret_cast(m_szStreamName)) < - 250) { - for (size_t i = 0; i < extCount; i++) { - strncpy(dotPos, extensions[i], 5); - app.DebugPrintf("Checking %s\n", m_szStreamName); - pFile = fopen( - reinterpret_cast(m_szStreamName), "rb"); - if (pFile) { - fclose(pFile); + for (const char* root : roots) { + for (const char* ext : extensions) { + char cand[512]; + + // if only i wrote a function that does EXACTLY + // that., nope ctrl c ctrl v it is + snprintf(cand, sizeof(cand), "%s%s%s%s", root, + folder, trackName, ext); + if (access(cand, F_OK) != -1) { + strncpy(m_szStreamName, cand, 511); + found = true; + break; + } + + snprintf(cand, sizeof(cand), "%s%s%s", root, + trackName, ext); + if (access(cand, F_OK) != -1) { + strncpy(m_szStreamName, cand, 511); found = true; break; } } - } - - if (!found) { - if (dotPos != nullptr) { - strncpy(dotPos, ".wav", 5); - } - app.DebugPrintf( - "WARNING: No audio file found for music ID %d " - "(tried .ogg, .mp3, .wav)\n", - m_musicID); - return; + if (found) break; } } - app.DebugPrintf("Starting streaming - %s\n", m_szStreamName); - m_openStreamThread = new C4JThread(OpenStreamThreadProc, this, - "OpenStreamThreadProc"); - m_openStreamThread->Run(); - m_StreamState = eMusicStreamState_Opening; + if (found) { + m_MusicType = isCD ? eMusicType_CD : eMusicType_Game; + m_StreamingAudioInfo.bIs3D = isCD; + SetIsPlayingStreamingGameMusic(!isCD); + SetIsPlayingStreamingCDMusic(isCD); + + m_openStreamThread = new C4JThread( + OpenStreamThreadProc, this, "OpenStreamThreadProc"); + m_openStreamThread->Run(); + m_StreamState = eMusicStreamState_Opening; + } else { + // Retry later if missing + m_iMusicDelay = 20 * 60; + } } break; @@ -651,64 +587,30 @@ void SoundEngine::playMusicTick() { delete m_openStreamThread; m_openStreamThread = nullptr; - app.DebugPrintf( - "OpenStreamThreadProc finished. m_musicStreamActive=%d\n", - m_musicStreamActive); - if (!m_musicStreamActive) { - const char* currentExt = - strrchr(reinterpret_cast(m_szStreamName), '.'); - if (currentExt && _stricmp(currentExt, ".wav") == 0) { - const bool isCD = (m_musicID >= m_iStream_CD_1); - const char* folder = isCD ? "cds/" : "music/"; - - int n = - sprintf_s(reinterpret_cast(m_szStreamName), - 512, "%s%s%s.wav", m_szMusicPath, folder, - m_szStreamFileA[m_musicID]); - - if (n > 0) { - FILE* pFile = fopen( - reinterpret_cast(m_szStreamName), "rb"); - if (pFile) { - fclose(pFile); - - m_openStreamThread = - new C4JThread(OpenStreamThreadProc, this, - "OpenStreamThreadProc"); - m_openStreamThread->Run(); - break; - } - } - } - m_StreamState = eMusicStreamState_Idle; break; } + ma_sound_set_spatialization_enabled( + &m_musicStream, + m_StreamingAudioInfo.bIs3D ? MA_TRUE : MA_FALSE); if (m_StreamingAudioInfo.bIs3D) { - ma_sound_set_spatialization_enabled(&m_musicStream, - MA_TRUE); ma_sound_set_position( &m_musicStream, m_StreamingAudioInfo.x, m_StreamingAudioInfo.y, m_StreamingAudioInfo.z); - } else { - ma_sound_set_spatialization_enabled(&m_musicStream, - MA_FALSE); } ma_sound_set_pitch(&m_musicStream, m_StreamingAudioInfo.pitch); - - float finalVolume = - m_StreamingAudioInfo.volume * getMasterMusicVolume(); - - ma_sound_set_volume(&m_musicStream, finalVolume); - ma_result startResult = ma_sound_start(&m_musicStream); - app.DebugPrintf("ma_sound_start result: %d\n", startResult); + ma_sound_set_volume( + &m_musicStream, + m_StreamingAudioInfo.volume * getMasterMusicVolume()); + ma_sound_start(&m_musicStream); m_StreamState = eMusicStreamState_Playing; } break; + case eMusicStreamState_OpeningCancel: if (!m_openStreamThread->isRunning()) { delete m_openStreamThread; @@ -716,200 +618,130 @@ void SoundEngine::playMusicTick() { m_StreamState = eMusicStreamState_Stop; } break; + case eMusicStreamState_Stop: if (m_musicStreamActive) { ma_sound_stop(&m_musicStream); ma_sound_uninit(&m_musicStream); m_musicStreamActive = false; } - SetIsPlayingStreamingCDMusic(false); SetIsPlayingStreamingGameMusic(false); - m_StreamState = eMusicStreamState_Idle; break; - case eMusicStreamState_Stopping: - break; - case eMusicStreamState_Play: - break; - case eMusicStreamState_Playing: { - static int frameCount = 0; - if (frameCount++ % 60 == 0) { - if (m_musicStreamActive) { - bool isPlaying = ma_sound_is_playing(&m_musicStream); - float vol = ma_sound_get_volume(&m_musicStream); - bool isAtEnd = ma_sound_at_end(&m_musicStream); - } - } - } + + case eMusicStreamState_Playing: if (GetIsPlayingStreamingGameMusic()) { - { - bool playerInEnd = false; - bool playerInNether = false; - Minecraft* pMinecraft = Minecraft::GetInstance(); - for (unsigned int i = 0; i < MAX_LOCAL_PLAYERS; ++i) { - if (pMinecraft->localplayers[i] != nullptr) { - if (pMinecraft->localplayers[i]->dimension == - LevelData::DIMENSION_END) { - playerInEnd = true; - } else if (pMinecraft->localplayers[i]->dimension == - LevelData::DIMENSION_NETHER) { - playerInNether = true; - } - } - } + bool playerInEnd = false, playerInNether = false; + Minecraft* pMinecraft = Minecraft::GetInstance(); - if (playerInEnd && !GetIsPlayingEndMusic()) { - m_StreamState = eMusicStreamState_Stop; - - // Set the end track - m_musicID = getMusicID(LevelData::DIMENSION_END); - SetIsPlayingEndMusic(true); - SetIsPlayingNetherMusic(false); - } else if (!playerInEnd && GetIsPlayingEndMusic()) { - if (playerInNether) { - m_StreamState = eMusicStreamState_Stop; - - // Set the end track - m_musicID = getMusicID(LevelData::DIMENSION_NETHER); - SetIsPlayingEndMusic(false); - SetIsPlayingNetherMusic(true); - } else { - m_StreamState = eMusicStreamState_Stop; - - // Set the end track - m_musicID = - getMusicID(LevelData::DIMENSION_OVERWORLD); - SetIsPlayingEndMusic(false); - SetIsPlayingNetherMusic(false); - } - } else if (playerInNether && !GetIsPlayingNetherMusic()) { - m_StreamState = eMusicStreamState_Stop; - // set the Nether track - m_musicID = getMusicID(LevelData::DIMENSION_NETHER); - SetIsPlayingNetherMusic(true); - SetIsPlayingEndMusic(false); - } else if (!playerInNether && GetIsPlayingNetherMusic()) { - if (playerInEnd) { - m_StreamState = eMusicStreamState_Stop; - // set the Nether track - m_musicID = getMusicID(LevelData::DIMENSION_END); - SetIsPlayingNetherMusic(false); - SetIsPlayingEndMusic(true); - } else { - m_StreamState = eMusicStreamState_Stop; - // set the Nether track - m_musicID = - getMusicID(LevelData::DIMENSION_OVERWORLD); - SetIsPlayingNetherMusic(false); - SetIsPlayingEndMusic(false); - } - } - - // volume change required? - if (m_musicStreamActive) { - float finalVolume = - m_StreamingAudioInfo.volume * fMusicVol; - - ma_sound_set_volume(&m_musicStream, finalVolume); + for (unsigned int i = 0; i < MAX_LOCAL_PLAYERS; ++i) { + if (pMinecraft->localplayers[i]) { + if (pMinecraft->localplayers[i]->dimension == + LevelData::DIMENSION_END) + playerInEnd = true; + else if (pMinecraft->localplayers[i]->dimension == + LevelData::DIMENSION_NETHER) + playerInNether = true; } } - } else { - // Music disc playing - if it's a 3D stream, then set the - // position - we don't have any streaming audio in the world - // that moves, so this isn't required unless we have more than - // one listener, and are setting the listening position to the - // origin and setting a fake position for the sound down the z - // axis - if (m_StreamingAudioInfo.bIs3D && m_validListenerCount > 1) { - int iClosestListener = 0; - float fClosestDist = 1e6f; - for (size_t i = 0; i < MAX_LOCAL_PLAYERS; i++) { - if (m_ListenerA[i].bValid) { - float dx = m_StreamingAudioInfo.x - - m_ListenerA[i].vPosition.x; - float dy = m_StreamingAudioInfo.y - - m_ListenerA[i].vPosition.y; - float dz = m_StreamingAudioInfo.z - - m_ListenerA[i].vPosition.z; - float dist = sqrtf(dx * dx + dy * dy + dz * dz); + // Handle Dimension Switching + bool needsStop = false; + if (playerInEnd && !GetIsPlayingEndMusic()) { + m_musicID = getMusicID(LevelData::DIMENSION_END); + SetIsPlayingEndMusic(true); + SetIsPlayingNetherMusic(false); + needsStop = true; + } else if (!playerInEnd && GetIsPlayingEndMusic()) { + m_musicID = + playerInNether + ? getMusicID(LevelData::DIMENSION_NETHER) + : getMusicID(LevelData::DIMENSION_OVERWORLD); + SetIsPlayingEndMusic(false); + SetIsPlayingNetherMusic(playerInNether); + needsStop = true; + } else if (playerInNether && !GetIsPlayingNetherMusic()) { + m_musicID = getMusicID(LevelData::DIMENSION_NETHER); + SetIsPlayingNetherMusic(true); + SetIsPlayingEndMusic(false); + needsStop = true; + } else if (!playerInNether && GetIsPlayingNetherMusic()) { + m_musicID = + playerInEnd + ? getMusicID(LevelData::DIMENSION_END) + : getMusicID(LevelData::DIMENSION_OVERWORLD); + SetIsPlayingNetherMusic(false); + SetIsPlayingEndMusic(playerInEnd); + needsStop = true; + } - if (dist < fClosestDist) { - fClosestDist = dist; - iClosestListener = i; - } + if (needsStop) m_StreamState = eMusicStreamState_Stop; + + // volume change required? + if (m_musicStreamActive) + ma_sound_set_volume( + &m_musicStream, + m_StreamingAudioInfo.volume * fMusicVol); + + } else if (m_StreamingAudioInfo.bIs3D && m_validListenerCount > 1 && + m_musicStreamActive) { + // incase we're splitscreen + float fClosestDist = 1e6f; + int iClosest = 0; + for (size_t i = 0; i < MAX_LOCAL_PLAYERS; i++) { + if (m_ListenerA[i].bValid) { + float dist = sqrtf(powf(m_StreamingAudioInfo.x - + m_ListenerA[i].vPosition.x, + 2) + + powf(m_StreamingAudioInfo.y - + m_ListenerA[i].vPosition.y, + 2) + + powf(m_StreamingAudioInfo.z - + m_ListenerA[i].vPosition.z, + 2)); + if (dist < fClosestDist) { + fClosestDist = dist; + iClosest = i; } } - - float relX = m_StreamingAudioInfo.x - - m_ListenerA[iClosestListener].vPosition.x; - float relY = m_StreamingAudioInfo.y - - m_ListenerA[iClosestListener].vPosition.y; - float relZ = m_StreamingAudioInfo.z - - m_ListenerA[iClosestListener].vPosition.z; - - if (m_musicStreamActive) { - ma_sound_set_position(&m_musicStream, relX, relY, relZ); - } } + ma_sound_set_position( + &m_musicStream, + m_StreamingAudioInfo.x - m_ListenerA[iClosest].vPosition.x, + m_StreamingAudioInfo.y - m_ListenerA[iClosest].vPosition.y, + m_StreamingAudioInfo.z - m_ListenerA[iClosest].vPosition.z); } - break; - case eMusicStreamState_Completed: { - // random delay of up to 3 minutes for music - m_iMusicDelay = random->nextInt( - 20 * 60 * 3); // random->nextInt(20 * 60 * 10) + 20 * 60 * 10; - // Check if we have a local player in The Nether or in The End, and - // play that music if they are - Minecraft* pMinecraft = Minecraft::GetInstance(); - bool playerInEnd = false; - bool playerInNether = false; + case eMusicStreamState_Completed: + m_iMusicDelay = random->nextInt(20 * 60 * 3); - for (unsigned int i = 0; i < MAX_LOCAL_PLAYERS; i++) { - if (pMinecraft->localplayers[i] != nullptr) { - if (pMinecraft->localplayers[i]->dimension == - LevelData::DIMENSION_END) { - playerInEnd = true; - } else if (pMinecraft->localplayers[i]->dimension == - LevelData::DIMENSION_NETHER) { - playerInNether = true; - } + int dim = LevelData::DIMENSION_OVERWORLD; + Minecraft* pMc = Minecraft::GetInstance(); + for (int i = 0; i < MAX_LOCAL_PLAYERS; i++) { + if (pMc->localplayers[i]) { + dim = pMc->localplayers[i]->dimension; + break; } } - if (playerInEnd) { - m_musicID = getMusicID(LevelData::DIMENSION_END); - SetIsPlayingEndMusic(true); - SetIsPlayingNetherMusic(false); - } else if (playerInNether) { - m_musicID = getMusicID(LevelData::DIMENSION_NETHER); - SetIsPlayingNetherMusic(true); - SetIsPlayingEndMusic(false); - } else { - m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD); - SetIsPlayingNetherMusic(false); - SetIsPlayingEndMusic(false); - } - + m_musicID = getMusicID(dim); + SetIsPlayingEndMusic(dim == LevelData::DIMENSION_END); + SetIsPlayingNetherMusic(dim == LevelData::DIMENSION_NETHER); m_StreamState = eMusicStreamState_Idle; - } break; + break; } // check the status of the stream - this is for when a track completes // rather than is stopped by the user action - if (m_musicStreamActive) { - if (!ma_sound_is_playing(&m_musicStream) && - ma_sound_at_end(&m_musicStream)) { - ma_sound_uninit(&m_musicStream); - m_musicStreamActive = false; - - SetIsPlayingStreamingCDMusic(false); - SetIsPlayingStreamingGameMusic(false); - - m_StreamState = eMusicStreamState_Completed; - } + if (m_musicStreamActive && !ma_sound_is_playing(&m_musicStream) && + ma_sound_at_end(&m_musicStream)) { + ma_sound_uninit(&m_musicStream); + m_musicStreamActive = false; + SetIsPlayingStreamingCDMusic(false); + SetIsPlayingStreamingGameMusic(false); + m_StreamState = eMusicStreamState_Completed; } } diff --git a/Minecraft.Client/Textures/BufferedImage.cpp b/Minecraft.Client/Textures/BufferedImage.cpp index 5509ea360..236690d5f 100644 --- a/Minecraft.Client/Textures/BufferedImage.cpp +++ b/Minecraft.Client/Textures/BufferedImage.cpp @@ -4,6 +4,12 @@ #include "../../Minecraft.World/Util/ArrayWithLength.h" #include "BufferedImage.h" +#if defined(__linux__) +#include +#endif +#include +#include + #ifdef _XBOX typedef struct { unsigned int filesz; @@ -28,7 +34,7 @@ BufferedImage::BufferedImage(int width, int height, int type) { data[0] = new int[width * height]; for (int i = 1; i < 10; i++) { - data[i] = NULL; + data[i] = nullptr; } this->width = width; this->height = height; @@ -43,136 +49,125 @@ void BufferedImage::ByteFlip4(unsigned int& data) { // the compression method. Compression method 3 is a 32-bit image with only // 24-bits used (ie no alpha channel) whereas method 0 is a full 32-bit image // with a valid alpha channel. + +// 4jcraft: mostly rewrote this function BufferedImage::BufferedImage(const std::wstring& File, - bool filenameHasExtension /*=false*/, - bool bTitleUpdateTexture /*=false*/, - const std::wstring& drive /*=L""*/) { - HRESULT hr; - std::wstring wDrive; - std::wstring filePath; - filePath = File; + bool filenameHasExtension, + bool bTitleUpdateTexture, + const std::wstring& drive) { + HRESULT hr = -1; + std::wstring filePath = File; - wDrive = drive; - if (wDrive.empty()) { -#ifdef _XBOX - if (bTitleUpdateTexture) { - // Make the content package point to to the UPDATE: drive is needed -#ifdef _TU_BUILD - wDrive = L"UPDATE:\\"; -#else + // turn that \ upside down! (grace ref) + for (size_t i = 0; i < filePath.length(); ++i) { + if (filePath[i] == L'\\') filePath[i] = L'/'; + } + for (int l = 0; l < 10; l++) data[l] = nullptr; - wDrive = L"GAME:\\res\\TitleUpdate\\"; -#endif - } else { - wDrive = L"GAME:\\"; + // clean the filename + std::wstring baseName = filePath; + if (!filenameHasExtension) { + if (baseName.size() > 4 && + baseName.substr(baseName.size() - 4) == L".png") { + baseName = baseName.substr(0, baseName.size() - 4); } -#else - -#ifdef __PS3__ - - char* pchUsrDir; - if (app.GetBootedFromDiscPatch()) { - const char* pchTextureName = wstringtofilename(File); - pchUsrDir = app.GetBDUsrDirPath(pchTextureName); - } else { - pchUsrDir = getUsrDirPath(); - } - - std::wstring wstr(pchUsrDir, pchUsrDir + strlen(pchUsrDir)); - - if (bTitleUpdateTexture) { - // Make the content package point to to the UPDATE: drive is needed - wDrive = wstr + L"\\Common\\res\\TitleUpdate\\"; - } else { - wDrive = wstr + L"/Common/"; - } -#elif __PSVITA__ - - /*char *pchUsrDir=getUsrDirPath(); - - wstring wstr (pchUsrDir, pchUsrDir+strlen(pchUsrDir)); - - if(bTitleUpdateTexture) - { - // Make the content package point to to the UPDATE: drive is - needed wDrive= wstr + L"\\Common\\res\\TitleUpdate\\"; - } - else - { - wDrive= wstr + L"/Common/"; - }*/ - - if (bTitleUpdateTexture) { - // Make the content package point to to the UPDATE: drive is needed - wDrive = L"Common\\res\\TitleUpdate\\"; - } else { - wDrive = L"Common/"; - } -#else - if (bTitleUpdateTexture) { - // Make the content package point to to the UPDATE: drive is needed - wDrive = L"Common\\res\\TitleUpdate\\"; - } else { - wDrive = L"Common/"; - } -#endif - -#endif } - for (int l = 0; l < 10; l++) { - data[l] = NULL; - } + // avoid // mess + while (!baseName.empty() && baseName[0] == L'/') + baseName = baseName.substr(1); + if (baseName.find(L"res/") == 0) baseName = baseName.substr(4); + // bad code alert + // loops through stuff on the drives because i don't fucking know what 4j + // did with the paths and i don't to break it.. for (int l = 0; l < 10; l++) { - std::wstring name; - std::wstring mipMapPath = L""; - if (l != 0) { - mipMapPath = L"MipMapLevel" + _toString(l + 1); - } - if (filenameHasExtension) { - name = wDrive + L"res" + filePath.substr(0, filePath.length()); - } else { - name = wDrive + L"res" + filePath.substr(0, filePath.length() - 4) + - mipMapPath + L".png"; + std::wstring mipSuffix = + (l != 0) ? L"MipMapLevel" + _toString(l + 1) : L""; + std::wstring fileName = baseName + mipSuffix + L".png"; + + bool foundOnDisk = false; + std::wstring finalPath; + + // i tried everything i can think of. + std::vector searchPaths = { + L"build/Minecraft.Client/Common/res/TitleUpdate/res/" + fileName, + L"build/Minecraft.Client/Common/res/" + fileName, + L"build/Minecraft.Client/Common/Media/" + fileName, + L"Common/res/TitleUpdate/res/" + fileName, + L"Common/res/" + fileName, + L"Minecraft.Assets/Common/res/TitleUpdate/res/" + fileName}; + + if (!drive.empty()) { + std::wstring drivePath = drive; + if (drivePath.back() != L'/') drivePath += L'/'; + searchPaths.push_back(drivePath + fileName); + searchPaths.push_back(drivePath + L"res/" + fileName); } - const char* pchTextureName = wstringtofilename(name); + for (auto& attempt : searchPaths) { + size_t p; + while ((p = attempt.find(L"//")) != std::wstring::npos) + attempt.replace(p, 2, L"/"); -#ifndef _CONTENT_PACKAGE - app.DebugPrintf("\n--- Loading TEXTURE - %s\n\n", pchTextureName); +#if defined(__linux__) + if (access(wstringtofilename(attempt), F_OK) != -1) { + finalPath = attempt; + foundOnDisk = true; + break; + } #endif + } D3DXIMAGE_INFO ImageInfo; ZeroMemory(&ImageInfo, sizeof(D3DXIMAGE_INFO)); - hr = - RenderManager.LoadTextureData(pchTextureName, &ImageInfo, &data[l]); - if (hr != ERROR_SUCCESS) { - // 4J - If we haven't loaded the non-mipmap version then exit the - // game - if (l == 0) { - app.FatalLoadError(); + if (foundOnDisk) { + hr = RenderManager.LoadTextureData(wstringtofilename(finalPath), + &ImageInfo, &data[l]); + } else { + // if everything fails just try the archive maybe theres something + // in it + std::wstring archiveKey = L"res/" + fileName; + if (app.hasArchiveFile(archiveKey)) { + byteArray ba = app.getArchiveFile(archiveKey); + hr = RenderManager.LoadTextureData(ba.data, ba.length, + &ImageInfo, &data[l]); } - return; } - if (l == 0) { - width = ImageInfo.Width; - height = ImageInfo.Height; + if (hr == ERROR_SUCCESS) { + if (l == 0) { + width = ImageInfo.Width; + height = ImageInfo.Height; + } + } else { + if (l == 0) { + app.DebugPrintf("[Texture Warning] Missing asset: %S\n", + fileName.c_str()); + // We MUST initialize width/height to avoid the program being a + // crybaby and crash + width = 1; + height = 1; + // Create a tiny missingno buffer so the rest of the game loads + // without crashing up + data[0] = new int[1]; + data[0][0] = 0xFFFF00FF; + } + break; } } } BufferedImage::BufferedImage(DLCPack* dlcPack, const std::wstring& File, - bool filenameHasExtension /*= false*/) { + bool filenameHasExtension) { HRESULT hr; std::wstring filePath = File; - std::uint8_t* pbData = NULL; + std::uint8_t* pbData = nullptr; std::uint32_t dataBytes = 0; for (int l = 0; l < 10; l++) { - data[l] = NULL; + data[l] = nullptr; } for (int l = 0; l < 10; l++) { @@ -199,12 +194,8 @@ BufferedImage::BufferedImage(DLCPack* dlcPack, const std::wstring& File, DLCFile* dlcFile = dlcPack->getFile(DLCManager::e_DLCType_All, name); pbData = dlcFile->getData(dataBytes); - if (pbData == NULL || dataBytes == 0) { - // 4J - If we haven't loaded the non-mipmap version then exit the - // game - if (l == 0) { - app.FatalLoadError(); - } + if (pbData == nullptr || dataBytes == 0) { + if (l == 0) app.FatalLoadError(); return; } @@ -214,10 +205,54 @@ BufferedImage::BufferedImage(DLCPack* dlcPack, const std::wstring& File, &data[l]); if (hr != ERROR_SUCCESS) { - // 4J - If we haven't loaded the non-mipmap version then exit the - // game if (l == 0) { - app.FatalLoadError(); + std::wstring wname = L"res" + + filePath.substr(0, filePath.length() - 4) + + L".png"; + std::vector candidates; + candidates.push_back(wname); + if (wname.rfind(L"Common/res/", 0) == 0) { + candidates.push_back( + wname.substr(std::wstring(L"Common/res/").length())); + } + candidates.push_back(filePath); + if (!filePath.empty() && filePath[0] == L'/') + candidates.push_back(filePath.substr(1)); + + std::wstring baseName2; + size_t posSlash2 = filePath.find_last_of(L"/\\"); + if (posSlash2 == std::wstring::npos) + baseName2 = filePath; + else + baseName2 = filePath.substr(posSlash2 + 1); + // same thing but for the fonts.. i found a way to make this + // less horrible but im lazy sorry :/ + if (!baseName2.empty()) { + candidates.insert(candidates.begin(), + L"Common/Res/Font/" + baseName2); + candidates.push_back(L"font/" + baseName2); + candidates.push_back(L"font\\" + baseName2); + candidates.push_back(L"res/font/" + baseName2); + candidates.push_back(L"Common/res/font/" + baseName2); + candidates.push_back(L"Common/Res/Font/" + baseName2); + } + + bool loaded = false; + for (auto& key : candidates) { + if (key.empty()) continue; + if (app.hasArchiveFile(key)) { + byteArray ba = app.getArchiveFile(key); + if (ba.data != nullptr && ba.length > 0) { + hr = RenderManager.LoadTextureData( + ba.data, ba.length, &ImageInfo, &data[l]); + if (hr == ERROR_SUCCESS) { + loaded = true; + break; + } + } + } + } + if (!loaded) app.FatalLoadError(); } return; } @@ -230,9 +265,8 @@ BufferedImage::BufferedImage(DLCPack* dlcPack, const std::wstring& File, } BufferedImage::BufferedImage(std::uint8_t* pbData, std::uint32_t dataBytes) { - int iCurrentByte = 0; for (int l = 0; l < 10; l++) { - data[l] = NULL; + data[l] = nullptr; } D3DXIMAGE_INFO ImageInfo; @@ -273,7 +307,7 @@ int* BufferedImage::getData() { return data[0]; } int* BufferedImage::getData(int level) { return data[level]; } -Graphics* BufferedImage::getGraphics() { return NULL; } +Graphics* BufferedImage::getGraphics() { return nullptr; } // Returns the transparency. Returns either OPAQUE, BITMASK, or TRANSLUCENT. // Specified by: @@ -299,14 +333,15 @@ BufferedImage* BufferedImage::getSubimage(int x, int y, int w, int h) { this->getRGB(x, y, w, h, arrayWrapper, 0, w); int level = 1; - while (getData(level) != NULL) { + // prevent overflow + while (level < 10 && getData(level) != nullptr) { int ww = w >> level; int hh = h >> level; int xx = x >> level; int yy = y >> level; img->data[level] = new int[ww * hh]; - intArray arrayWrapper(img->data[level], ww * hh); - this->getRGB(xx, yy, ww, hh, arrayWrapper, 0, ww, level); + intArray levelWrapper(img->data[level], ww * hh); + this->getRGB(xx, yy, ww, hh, levelWrapper, 0, ww, level); ++level; } @@ -324,7 +359,8 @@ void BufferedImage::preMultiplyAlpha() { int b = 0; int total = width * height; - for (unsigned int i = 0; i < total; ++i) { + // why was it unsigned?? + for (int i = 0; i < total; ++i) { cur = curData[i]; alpha = (cur >> 24) & 0xff; r = ((cur >> 16) & 0xff) * (float)alpha / 255; diff --git a/Minecraft.World/IO/Files/File.cpp b/Minecraft.World/IO/Files/File.cpp index e5bff5c98..7567d80d1 100644 --- a/Minecraft.World/IO/Files/File.cpp +++ b/Minecraft.World/IO/Files/File.cpp @@ -57,33 +57,44 @@ File::File(const File& parent, const std::wstring& child) { // Creates a new File instance by converting the given pathname string into an // abstract pathname. -File::File(const std::wstring& pathname) //: parent( NULL ) -{ - // #ifndef _CONTENT_PACKAGE - // char buf[256]; - // wcstombs(buf, pathname.c_str(), 256); - // printf("File::File - %s\n",buf); - // #endif - if (pathname.empty()) - m_abstractPathName = std::wstring(L""); - else - m_abstractPathName = pathname; +File::File(const std::wstring& pathname) { + if (pathname.empty()) { + m_abstractPathName = L""; + return; + } + // same thing as in bufferedoverflow + std::wstring fixedPath = pathname; + + for (size_t i = 0; i < fixedPath.length(); ++i) { + if (fixedPath[i] == L'\\') fixedPath[i] = L'/'; + } + + size_t dpos; + while ((dpos = fixedPath.find(L"//")) != std::wstring::npos) + fixedPath.erase(dpos, 1); + if (fixedPath.find(L"GAME:/") == 0) fixedPath = fixedPath.substr(6); + + m_abstractPathName = fixedPath; #if defined(__linux__) // If this is a relative path and it doesn't exist in the CWD, try to - // resolve it relative to the executable directory + // resolve it relative to the executable directory if (!m_abstractPathName.empty() && m_abstractPathName[0] != L'/') { const char* native = wstringtofilename(m_abstractPathName); if (access(native, F_OK) == -1) { char exePathBuf[PATH_MAX]; - ssize_t exeLen = readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); + ssize_t exeLen = + readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); if (exeLen != -1) { exePathBuf[exeLen] = '\0'; std::string exePathStr(exePathBuf); size_t pos = exePathStr.find_last_of('/'); - std::string exeDir = (pos == std::string::npos) ? std::string(".") : exePathStr.substr(0, pos); + std::string exeDir = (pos == std::string::npos) + ? std::string(".") + : exePathStr.substr(0, pos); std::wstring exeDirW = convStringToWstring(exeDir); - std::wstring candidate = exeDirW + pathSeparator + m_abstractPathName; + std::wstring candidate = + exeDirW + pathSeparator + m_abstractPathName; const char* candNative = wstringtofilename(candidate); if (access(candNative, F_OK) != -1) { m_abstractPathName = candidate; diff --git a/meson.build b/meson.build index cd2b7daa8..767f1a4f5 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('4jcraft-chucklegrounds', ['cpp', 'c'], +project('4jcraft', ['cpp', 'c'], version : '0.1.0', meson_version: '>= 1.7', default_options : [ From 21fc2166ffe0cc3fd5a5579a748d2d88f154a33a Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 20:54:21 +0100 Subject: [PATCH 3/4] cleaned up, less hacky, more silly --- .../Platform/Common/Audio/SoundEngine.cpp | 244 +++++++----------- .../Platform/Common/Consoles_App.cpp | 12 +- Minecraft.Client/Textures/BufferedImage.cpp | 132 ++-------- Minecraft.Client/meson.build | 2 +- Minecraft.World/IO/Files/File.cpp | 59 +++-- Minecraft.World/Platform/PathHelper.h | 37 +++ 6 files changed, 186 insertions(+), 300 deletions(-) create mode 100644 Minecraft.World/Platform/PathHelper.h diff --git a/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp b/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp index fa53701b9..557bd4478 100644 --- a/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp +++ b/Minecraft.Client/Platform/Common/Audio/SoundEngine.cpp @@ -1,6 +1,6 @@ #include "../../Minecraft.World/Platform/stdafx.h" - #include "SoundEngine.h" +#include "PathHelper.h" #include "../Consoles_App.h" #include "../../Minecraft.Client/Player/MultiPlayerLocalPlayer.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.h" @@ -188,69 +188,51 @@ void SoundEngine::init(Options* pOptions) { m_bSystemMusicPlaying = false; } void SoundEngine::destroy() { ma_engine_uninit(&m_engine); } + void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch) { if (iSound == -1) return; + char szId[256]; + wcstombs(szId, wchSoundNames[iSound], 255); + for (int i = 0; szId[i]; i++) + if (szId[i] == '.') szId[i] = '/'; - char szIdentifier[256]; - wcstombs(szIdentifier, wchSoundNames[iSound], 255); - - // dot to folder structure (example step.grass -> step/grass) - for (int i = 0; szIdentifier[i]; i++) { - if (szIdentifier[i] == '.') szIdentifier[i] = '/'; - } - // YES I KNOW SOUNDNAMES.CPP EXISTS. - const char* extensions[] = {".ogg", ".wav", ".mp3"}; - const char* roots[] = {"Sound/Minecraft/", - "build/Minecraft.Client/Sound/Minecraft/", - "Common/Sound/Minecraft/", + std::string base = PathHelper::GetExecutableDirA() + "/"; + const char* roots[] = {"Sound/Minecraft/", "Common/Sound/Minecraft/", "Common/res/TitleUpdate/res/Sound/Minecraft/"}; - char finalPath[512] = {0}; bool found = false; - // search for variants (grass1, grass2, etc.) - // this is hacky + for (const char* root : roots) { - for (const char* ext : extensions) { - int maxVariant = 0; + std::string fullRoot = base + root; + for (const char* ext : {".ogg", ".wav"}) { + int count = 0; for (int i = 1; i <= 16; i++) { - char tryPath[512]; - snprintf(tryPath, sizeof(tryPath), "%s%s%d%s", root, - szIdentifier, i, ext); - if (access(tryPath, F_OK) != -1) - maxVariant = i; + char tryP[512]; + snprintf(tryP, 512, "%s%s%d%s", fullRoot.c_str(), szId, i, ext); + if (access(tryP, F_OK) != -1) + count = i; else break; } - - if (maxVariant > 0) { - int chosen = (rand() % maxVariant) + 1; - snprintf(finalPath, sizeof(finalPath), "%s%s%d%s", root, - szIdentifier, chosen, ext); + if (count > 0) { + snprintf(finalPath, 512, "%s%s%d%s", fullRoot.c_str(), szId, + (rand() % count) + 1, ext); + found = true; + break; + } + char tryP[512]; + snprintf(tryP, 512, "%s%s%s", fullRoot.c_str(), szId, ext); + if (access(tryP, F_OK) != -1) { + strncpy(finalPath, tryP, 511); found = true; break; } } if (found) break; } - if (!found) { - for (const char* root : roots) { - for (const char* ext : extensions) { - char tryPath[512]; - snprintf(tryPath, sizeof(tryPath), "%s%s%s", root, szIdentifier, - ext); - if (access(tryPath, F_OK) != -1) { - strncpy(finalPath, tryPath, 511); - found = true; - break; - } - } - if (found) break; - } - } if (!found) return; - MiniAudioSound* s = new MiniAudioSound(); memset(&s->info, 0, sizeof(AUDIO_INFO)); s->info.x = x; @@ -266,43 +248,38 @@ void SoundEngine::play(int iSound, float x, float y, float z, float volume, ma_sound_set_min_distance(&s->sound, 2.0f); ma_sound_set_max_distance(&s->sound, 48.0f); ma_sound_set_volume(&s->sound, volume * m_MasterEffectsVolume); - ma_sound_set_pitch(&s->sound, pitch); ma_sound_set_position(&s->sound, x, y, z); ma_sound_start(&s->sound); m_activeSounds.push_back(s); - } else { + } else delete s; - } } void SoundEngine::playUI(int iSound, float volume, float pitch) { char szIdentifier[256]; - if (iSound >= eSFX_MAX) { + if (iSound >= eSFX_MAX) wcstombs(szIdentifier, wchSoundNames[iSound], 255); - } else { + else wcstombs(szIdentifier, wchUISoundNames[iSound], 255); - } - - for (int i = 0; szIdentifier[i]; i++) { + for (int i = 0; szIdentifier[i]; i++) if (szIdentifier[i] == '.') szIdentifier[i] = '/'; - } - // ui sfx also WHO WHO THAT EWWW - const char* extensions[] = {".ogg", ".wav", ".mp3"}; - const char* roots[] = {"Sound/Minecraft/UI/", "Sound/Minecraft/", - "build/Minecraft.Client/Sound/Minecraft/UI/", - "build/Minecraft.Client/Sound/Minecraft/", - "Common/Sound/Minecraft/UI/"}; - + std::string base = PathHelper::GetExecutableDirA() + "/"; + const char* roots[] = { + "Sound/Minecraft/UI/", + "Sound/Minecraft/", + "Common/Sound/Minecraft/UI/", + "Common/Sound/Minecraft/", + }; char finalPath[512] = {0}; bool found = false; for (const char* root : roots) { - for (const char* ext : extensions) { - char tryPath[512]; - snprintf(tryPath, sizeof(tryPath), "%s%s%s", root, szIdentifier, + for (const char* ext : {".ogg", ".wav", ".mp3"}) { + char tryP[512]; + snprintf(tryP, 512, "%s%s%s%s", base.c_str(), root, szIdentifier, ext); - if (access(tryPath, F_OK) != -1) { - strncpy(finalPath, tryPath, 511); + if (access(tryP, F_OK) != -1) { + strncpy(finalPath, tryP, 511); found = true; break; } @@ -311,7 +288,6 @@ void SoundEngine::playUI(int iSound, float volume, float pitch) { } if (!found) return; - MiniAudioSound* s = new MiniAudioSound(); memset(&s->info, 0, sizeof(AUDIO_INFO)); s->info.volume = volume; @@ -325,9 +301,8 @@ void SoundEngine::playUI(int iSound, float volume, float pitch) { ma_sound_set_pitch(&s->sound, pitch); ma_sound_start(&s->sound); m_activeSounds.push_back(s); - } else { + } else delete s; - } } int SoundEngine::getMusicID(int iDomain) { @@ -344,9 +319,10 @@ int SoundEngine::getMusicID(int iDomain) { if (pMinecraft->skins->isUsingDefaultSkin()) { switch (iDomain) { case LevelData::DIMENSION_END: - // the end isn't random - it has different music depending on - // whether the dragon is alive or not, but we've not added the - // dead dragon music yet + // the end isn't random - it has different music depending + // whether the dragon is alive or not, but we've not + // added the dead dragon music yet + // haha they said wheter return m_iStream_End_Min; case LevelData::DIMENSION_NETHER: return GetRandomishTrack(m_iStream_Nether_Min, @@ -464,7 +440,8 @@ int SoundEngine::OpenStreamThreadProc(void* lpParameter) { if (result != MA_SUCCESS) { app.DebugPrintf( - "SoundEngine::OpenStreamThreadProc - Failed to open stream: %s\n", + "SoundEngine::OpenStreamThreadProc - Failed to open stream: " + "%s\n", soundEngine->m_szStreamName); return 0; } @@ -476,7 +453,6 @@ int SoundEngine::OpenStreamThreadProc(void* lpParameter) { return 0; } - void SoundEngine::playMusicTick() { static float fMusicVol = 0.0f; fMusicVol = getMasterMusicVolume(); @@ -487,96 +463,51 @@ void SoundEngine::playMusicTick() { m_iMusicDelay--; return; } - - if (m_musicStreamActive) { - m_StreamState = eMusicStreamState_Playing; - return; - } - if (m_musicID != -1) { + std::string base = PathHelper::GetExecutableDirA() + "/"; bool isCD = (m_musicID >= m_iStream_CD_1); const char* folder = isCD ? "cds/" : "music/"; - const char* trackName = m_szStreamFileA[m_musicID]; - const char* extensions[] = {".ogg", ".mp3", ".wav"}; - + const char* track = m_szStreamFileA[m_musicID]; bool found = false; m_szStreamName[0] = '\0'; - // DLC Mashup pack check - if (Minecraft::GetInstance() - ->skins->getSelected() - ->hasAudio()) { - TexturePack* pTexPack = - Minecraft::GetInstance()->skins->getSelected(); - DLCPack* pack = - ((DLCTexturePack*)pTexPack)->getDLCInfoParentPack(); - DLCAudioFile* dlcAudioFile = (DLCAudioFile*)pack->getFile( - DLCManager::e_DLCType_Audio, 0); + const char* roots[] = {"Common/music/", "music/", "./"}; - if (!isCD) { - m_MusicType = eMusicType_Game; - m_StreamingAudioInfo.bIs3D = false; - - wstring& wstrSoundName = - dlcAudioFile->GetSoundName(m_musicID); - char szName[255]; - wcstombs(szName, wstrSoundName.c_str(), 255); - - std::string strFile = - "TPACK:\\Data\\" + string(szName) + ".wav"; - std::string mountedPath = - StorageManager.GetMountedPath(strFile); - strcpy(m_szStreamName, mountedPath.c_str()); - - if (access(m_szStreamName, F_OK) != -1) found = true; - } - } - - // we're doing this again, daring aren't we - if (!found) { - const char* roots[] = { - "build/Minecraft.Client/Common/music/", - "build/Minecraft.Client/music/", "Common/music/", - "music/", "./"}; - - for (const char* root : roots) { - for (const char* ext : extensions) { - char cand[512]; - - // if only i wrote a function that does EXACTLY - // that., nope ctrl c ctrl v it is - snprintf(cand, sizeof(cand), "%s%s%s%s", root, - folder, trackName, ext); - if (access(cand, F_OK) != -1) { - strncpy(m_szStreamName, cand, 511); - found = true; - break; - } - - snprintf(cand, sizeof(cand), "%s%s%s", root, - trackName, ext); - if (access(cand, F_OK) != -1) { - strncpy(m_szStreamName, cand, 511); - found = true; - break; - } + for (const char* r : roots) { + for (const char* e : {".ogg", ".mp3", ".wav"}) { + char c[512]; + // try with folder prefix (music/ or cds/) + snprintf(c, 512, "%s%s%s%s%s", base.c_str(), r, folder, + track, e); + if (access(c, F_OK) != -1) { + strncpy(m_szStreamName, c, 511); + found = true; + break; + } + // try without folder prefix + snprintf(c, 512, "%s%s%s%s", base.c_str(), r, track, e); + if (access(c, F_OK) != -1) { + strncpy(m_szStreamName, c, 511); + found = true; + break; } - if (found) break; } + if (found) break; } if (found) { - m_MusicType = isCD ? eMusicType_CD : eMusicType_Game; - m_StreamingAudioInfo.bIs3D = isCD; SetIsPlayingStreamingGameMusic(!isCD); SetIsPlayingStreamingCDMusic(isCD); - m_openStreamThread = new C4JThread( OpenStreamThreadProc, this, "OpenStreamThreadProc"); m_openStreamThread->Run(); m_StreamState = eMusicStreamState_Opening; } else { - // Retry later if missing + app.DebugPrintf( + "[SoundEngine] oh noes couldn't find music track '%s', " + "retrying " + "in 1min\n", + track); m_iMusicDelay = 20 * 60; } } @@ -686,7 +617,6 @@ void SoundEngine::playMusicTick() { } else if (m_StreamingAudioInfo.bIs3D && m_validListenerCount > 1 && m_musicStreamActive) { - // incase we're splitscreen float fClosestDist = 1e6f; int iClosest = 0; for (size_t i = 0; i < MAX_LOCAL_PLAYERS; i++) { @@ -716,18 +646,19 @@ void SoundEngine::playMusicTick() { case eMusicStreamState_Completed: m_iMusicDelay = random->nextInt(20 * 60 * 3); - - int dim = LevelData::DIMENSION_OVERWORLD; - Minecraft* pMc = Minecraft::GetInstance(); - for (int i = 0; i < MAX_LOCAL_PLAYERS; i++) { - if (pMc->localplayers[i]) { - dim = pMc->localplayers[i]->dimension; - break; + { + int dim = LevelData::DIMENSION_OVERWORLD; + Minecraft* pMc = Minecraft::GetInstance(); + for (int i = 0; i < MAX_LOCAL_PLAYERS; i++) { + if (pMc->localplayers[i]) { + dim = pMc->localplayers[i]->dimension; + break; + } } + m_musicID = getMusicID(dim); + SetIsPlayingEndMusic(dim == LevelData::DIMENSION_END); + SetIsPlayingNetherMusic(dim == LevelData::DIMENSION_NETHER); } - m_musicID = getMusicID(dim); - SetIsPlayingEndMusic(dim == LevelData::DIMENSION_END); - SetIsPlayingNetherMusic(dim == LevelData::DIMENSION_NETHER); m_StreamState = eMusicStreamState_Idle; break; } @@ -2134,7 +2065,8 @@ F32 AILCALLBACK custom_falloff_function(HSAMPLE S, F32 distance, } #endif -// Universal, these functions shouldn't need platform specific implementations +// Universal, these functions shouldn't need platform specific +// implementations void SoundEngine::updateMusicVolume(float fVal) { m_MasterMusicVolume = fVal; } void SoundEngine::updateSystemMusicPlaying(bool isPlaying) { m_bSystemMusicPlaying = isPlaying; diff --git a/Minecraft.Client/Platform/Common/Consoles_App.cpp b/Minecraft.Client/Platform/Common/Consoles_App.cpp index 5eb204c95..afe871205 100644 --- a/Minecraft.Client/Platform/Common/Consoles_App.cpp +++ b/Minecraft.Client/Platform/Common/Consoles_App.cpp @@ -54,7 +54,7 @@ #include "../Minecraft.Client/Minecraft.h" #if defined(__linux__) #include -#include +#include #endif #ifdef _XBOX #include "../Minecraft.Client/Platform/Xbox/GameConfig/Minecraft.spa.h" @@ -4606,18 +4606,22 @@ void CMinecraftApp::loadMediaArchive() { if (!mediapath.empty()) { // (check file.cpp) // try to load the archive relative to the executable - // directory first. + // directory first. // If that fails, fall back to the current working // directory (original behavior) // if everything fails, may god help you #if defined(__linux__) + // THIS CAN USE PATHHELPER.h char exePathBuf[PATH_MAX]; - ssize_t exeLen = readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); + ssize_t exeLen = + readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); if (exeLen != -1) { exePathBuf[exeLen] = '\0'; std::string exePathStr(exePathBuf); size_t pos = exePathStr.find_last_of('/'); - std::string exeDir = (pos == std::string::npos) ? std::string(".") : exePathStr.substr(0, pos); + std::string exeDir = (pos == std::string::npos) + ? std::string(".") + : exePathStr.substr(0, pos); std::wstring exeDirW = convStringToWstring(exeDir.c_str()); std::wstring candidate = exeDirW + File::pathSeparator + mediapath; if (File(candidate).exists()) { diff --git a/Minecraft.Client/Textures/BufferedImage.cpp b/Minecraft.Client/Textures/BufferedImage.cpp index 236690d5f..7981e60d7 100644 --- a/Minecraft.Client/Textures/BufferedImage.cpp +++ b/Minecraft.Client/Textures/BufferedImage.cpp @@ -1,9 +1,9 @@ #include "../Platform/stdafx.h" #include "../../Minecraft.World/Util/StringHelpers.h" #include "Textures.h" +#include "PathHelper.h" #include "../../Minecraft.World/Util/ArrayWithLength.h" #include "BufferedImage.h" - #if defined(__linux__) #include #endif @@ -58,13 +58,11 @@ BufferedImage::BufferedImage(const std::wstring& File, HRESULT hr = -1; std::wstring filePath = File; - // turn that \ upside down! (grace ref) for (size_t i = 0; i < filePath.length(); ++i) { if (filePath[i] == L'\\') filePath[i] = L'/'; } for (int l = 0; l < 10; l++) data[l] = nullptr; - // clean the filename std::wstring baseName = filePath; if (!filenameHasExtension) { if (baseName.size() > 4 && @@ -73,50 +71,36 @@ BufferedImage::BufferedImage(const std::wstring& File, } } - // avoid // mess - while (!baseName.empty() && baseName[0] == L'/') + while (!baseName.empty() && (baseName[0] == L'/' || baseName[0] == L'\\')) baseName = baseName.substr(1); if (baseName.find(L"res/") == 0) baseName = baseName.substr(4); - // bad code alert - // loops through stuff on the drives because i don't fucking know what 4j - // did with the paths and i don't to break it.. + std::wstring exeDir = PathHelper::GetExecutableDirW(); + for (int l = 0; l < 10; l++) { std::wstring mipSuffix = (l != 0) ? L"MipMapLevel" + _toString(l + 1) : L""; std::wstring fileName = baseName + mipSuffix + L".png"; - - bool foundOnDisk = false; std::wstring finalPath; + bool foundOnDisk = false; - // i tried everything i can think of. std::vector searchPaths = { - L"build/Minecraft.Client/Common/res/TitleUpdate/res/" + fileName, - L"build/Minecraft.Client/Common/res/" + fileName, - L"build/Minecraft.Client/Common/Media/" + fileName, - L"Common/res/TitleUpdate/res/" + fileName, - L"Common/res/" + fileName, - L"Minecraft.Assets/Common/res/TitleUpdate/res/" + fileName}; - - if (!drive.empty()) { - std::wstring drivePath = drive; - if (drivePath.back() != L'/') drivePath += L'/'; - searchPaths.push_back(drivePath + fileName); - searchPaths.push_back(drivePath + L"res/" + fileName); - } + exeDir + L"/Common/res/TitleUpdate/res/" + fileName, + exeDir + L"/Common/res/" + fileName, + exeDir + L"/Common/Media/Graphics/" + fileName, + exeDir + L"/Common/Media/font/" + fileName, + exeDir + L"/Common/res/font/" + fileName, + exeDir + L"/Common/Media/" + fileName}; for (auto& attempt : searchPaths) { size_t p; while ((p = attempt.find(L"//")) != std::wstring::npos) attempt.replace(p, 2, L"/"); - -#if defined(__linux__) if (access(wstringtofilename(attempt), F_OK) != -1) { finalPath = attempt; foundOnDisk = true; break; } -#endif } D3DXIMAGE_INFO ImageInfo; @@ -126,8 +110,6 @@ BufferedImage::BufferedImage(const std::wstring& File, hr = RenderManager.LoadTextureData(wstringtofilename(finalPath), &ImageInfo, &data[l]); } else { - // if everything fails just try the archive maybe theres something - // in it std::wstring archiveKey = L"res/" + fileName; if (app.hasArchiveFile(archiveKey)) { byteArray ba = app.getArchiveFile(archiveKey); @@ -143,14 +125,9 @@ BufferedImage::BufferedImage(const std::wstring& File, } } else { if (l == 0) { - app.DebugPrintf("[Texture Warning] Missing asset: %S\n", - fileName.c_str()); - // We MUST initialize width/height to avoid the program being a - // crybaby and crash + // safety dummy to prevent crash width = 1; height = 1; - // Create a tiny missingno buffer so the rest of the game loads - // without crashing up data[0] = new int[1]; data[0][0] = 0xFFFF00FF; } @@ -158,37 +135,25 @@ BufferedImage::BufferedImage(const std::wstring& File, } } } - BufferedImage::BufferedImage(DLCPack* dlcPack, const std::wstring& File, bool filenameHasExtension) { HRESULT hr; std::wstring filePath = File; std::uint8_t* pbData = nullptr; std::uint32_t dataBytes = 0; - - for (int l = 0; l < 10; l++) { - data[l] = nullptr; - } + for (int l = 0; l < 10; l++) data[l] = nullptr; for (int l = 0; l < 10; l++) { std::wstring name; - std::wstring mipMapPath = L""; - if (l != 0) { - mipMapPath = L"MipMapLevel" + _toString(l + 1); - } - if (filenameHasExtension) { - name = L"res" + filePath.substr(0, filePath.length()); - } else { - name = L"res" + filePath.substr(0, filePath.length() - 4) + - mipMapPath + L".png"; - } + std::wstring mipMapPath = + (l != 0) ? L"MipMapLevel" + _toString(l + 1) : L""; + name = L"res" + (filenameHasExtension + ? filePath + : filePath.substr(0, filePath.length() - 4) + + mipMapPath + L".png"); if (!dlcPack->doesPackContainFile(DLCManager::e_DLCType_All, name)) { - // 4J - If we haven't loaded the non-mipmap version then exit the - // game - if (l == 0) { - app.FatalLoadError(); - } + if (l == 0) app.FatalLoadError(); return; } @@ -200,64 +165,9 @@ BufferedImage::BufferedImage(DLCPack* dlcPack, const std::wstring& File, } D3DXIMAGE_INFO ImageInfo; - ZeroMemory(&ImageInfo, sizeof(D3DXIMAGE_INFO)); hr = RenderManager.LoadTextureData(pbData, dataBytes, &ImageInfo, &data[l]); - - if (hr != ERROR_SUCCESS) { - if (l == 0) { - std::wstring wname = L"res" + - filePath.substr(0, filePath.length() - 4) + - L".png"; - std::vector candidates; - candidates.push_back(wname); - if (wname.rfind(L"Common/res/", 0) == 0) { - candidates.push_back( - wname.substr(std::wstring(L"Common/res/").length())); - } - candidates.push_back(filePath); - if (!filePath.empty() && filePath[0] == L'/') - candidates.push_back(filePath.substr(1)); - - std::wstring baseName2; - size_t posSlash2 = filePath.find_last_of(L"/\\"); - if (posSlash2 == std::wstring::npos) - baseName2 = filePath; - else - baseName2 = filePath.substr(posSlash2 + 1); - // same thing but for the fonts.. i found a way to make this - // less horrible but im lazy sorry :/ - if (!baseName2.empty()) { - candidates.insert(candidates.begin(), - L"Common/Res/Font/" + baseName2); - candidates.push_back(L"font/" + baseName2); - candidates.push_back(L"font\\" + baseName2); - candidates.push_back(L"res/font/" + baseName2); - candidates.push_back(L"Common/res/font/" + baseName2); - candidates.push_back(L"Common/Res/Font/" + baseName2); - } - - bool loaded = false; - for (auto& key : candidates) { - if (key.empty()) continue; - if (app.hasArchiveFile(key)) { - byteArray ba = app.getArchiveFile(key); - if (ba.data != nullptr && ba.length > 0) { - hr = RenderManager.LoadTextureData( - ba.data, ba.length, &ImageInfo, &data[l]); - if (hr == ERROR_SUCCESS) { - loaded = true; - break; - } - } - } - } - if (!loaded) app.FatalLoadError(); - } - return; - } - - if (l == 0) { + if (hr == ERROR_SUCCESS && l == 0) { width = ImageInfo.Width; height = ImageInfo.Height; } diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index f81dddddd..6b43ab90c 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -95,7 +95,7 @@ endif client = executable('Minecraft.Client', client_sources + platform_sources + localisation[1], - include_directories : [include_directories('Platform', 'Platform/Linux/Iggy/include'),stb], + include_directories : [include_directories('Platform', 'Platform/Linux/Iggy/include', '../Minecraft.World/Platform'),stb], dependencies : client_dependencies, cpp_args : global_cpp_args + global_cpp_defs + [ '-DUNICODE', '-D_UNICODE', diff --git a/Minecraft.World/IO/Files/File.cpp b/Minecraft.World/IO/Files/File.cpp index 7567d80d1..b67d8db33 100644 --- a/Minecraft.World/IO/Files/File.cpp +++ b/Minecraft.World/IO/Files/File.cpp @@ -2,7 +2,7 @@ #include "FileFilter.h" #include "../../Level/Storage/McRegionLevelStorageSource.h" #include "File.h" - +#include "PathHelper.h" #if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__) #include #include @@ -57,52 +57,55 @@ File::File(const File& parent, const std::wstring& child) { // Creates a new File instance by converting the given pathname string into an // abstract pathname. + File::File(const std::wstring& pathname) { if (pathname.empty()) { m_abstractPathName = L""; return; } - // same thing as in bufferedoverflow - std::wstring fixedPath = pathname; + std::wstring fixedPath = pathname; for (size_t i = 0; i < fixedPath.length(); ++i) { if (fixedPath[i] == L'\\') fixedPath[i] = L'/'; } - size_t dpos; while ((dpos = fixedPath.find(L"//")) != std::wstring::npos) fixedPath.erase(dpos, 1); if (fixedPath.find(L"GAME:/") == 0) fixedPath = fixedPath.substr(6); - m_abstractPathName = fixedPath; #if defined(__linux__) - // If this is a relative path and it doesn't exist in the CWD, try to - // resolve it relative to the executable directory - if (!m_abstractPathName.empty() && m_abstractPathName[0] != L'/') { - const char* native = wstringtofilename(m_abstractPathName); - if (access(native, F_OK) == -1) { - char exePathBuf[PATH_MAX]; - ssize_t exeLen = - readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); - if (exeLen != -1) { - exePathBuf[exeLen] = '\0'; - std::string exePathStr(exePathBuf); - size_t pos = exePathStr.find_last_of('/'); - std::string exeDir = (pos == std::string::npos) - ? std::string(".") - : exePathStr.substr(0, pos); - std::wstring exeDirW = convStringToWstring(exeDir); - std::wstring candidate = - exeDirW + pathSeparator + m_abstractPathName; - const char* candNative = wstringtofilename(candidate); - if (access(candNative, F_OK) != -1) { - m_abstractPathName = candidate; - } - } + std::string request = wstringtofilename(m_abstractPathName); + while (!request.empty() && request[0] == '/') request.erase(0, 1); + if (request.find("res/") == 0) request.erase(0, 4); + + std::string exeDir = PathHelper::GetExecutableDirA(); + std::string fileName = request; + size_t lastSlash = fileName.find_last_of('/'); + if (lastSlash != std::string::npos) + fileName = fileName.substr(lastSlash + 1); + + const char* bases[] = {"/", + "/Common/res/TitleUpdate/res/", + "/Common/Media/", + "/Common/res/", + "/Common/", + "/Minecraft.Assets/"}; + + for (const char* base : bases) { + std::string tryFull = exeDir + base + request; + std::string tryFile = exeDir + base + fileName; + if (access(tryFull.c_str(), F_OK) != -1) { + m_abstractPathName = convStringToWstring(tryFull); + return; + } + if (access(tryFile.c_str(), F_OK) != -1) { + m_abstractPathName = convStringToWstring(tryFile); + return; } } #endif + #ifdef _WINDOWS64 std::string path = wstringtofilename(m_abstractPathName); std::string finalPath = StorageManager.GetMountedPath(path.c_str()); diff --git a/Minecraft.World/Platform/PathHelper.h b/Minecraft.World/Platform/PathHelper.h new file mode 100644 index 000000000..e02b755cd --- /dev/null +++ b/Minecraft.World/Platform/PathHelper.h @@ -0,0 +1,37 @@ +#pragma once +#include +#if defined(__linux__) +#include +#include +#endif + +namespace PathHelper { +inline std::wstring GetExecutableDirW() { +#if defined(__linux__) + char buffer[4096]; + ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len != -1) { + buffer[len] = '\0'; + std::string path(buffer); + size_t lastSlash = path.find_last_of('/'); + if (lastSlash != std::string::npos) + return std::wstring(path.begin(), path.begin() + lastSlash); + } +#endif + return L"."; +} + +inline std::string GetExecutableDirA() { +#if defined(__linux__) + char buffer[4096]; + ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len != -1) { + buffer[len] = '\0'; + std::string path(buffer); + size_t lastSlash = path.find_last_of('/'); + if (lastSlash != std::string::npos) return path.substr(0, lastSlash); + } +#endif + return "."; +} +} // namespace PathHelper \ No newline at end of file From 6be5f627efa4cd03746fd78d353952a06746e12c Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 20:57:46 +0100 Subject: [PATCH 4/4] make consoles_app use pathhelper :) --- .../Platform/Common/Consoles_App.cpp | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/Minecraft.Client/Platform/Common/Consoles_App.cpp b/Minecraft.Client/Platform/Common/Consoles_App.cpp index afe871205..95053b713 100644 --- a/Minecraft.Client/Platform/Common/Consoles_App.cpp +++ b/Minecraft.Client/Platform/Common/Consoles_App.cpp @@ -4585,7 +4585,6 @@ int CMinecraftApp::BannedLevelDialogReturned( return 0; } - void CMinecraftApp::loadMediaArchive() { std::wstring mediapath = L""; @@ -4604,31 +4603,12 @@ void CMinecraftApp::loadMediaArchive() { #endif if (!mediapath.empty()) { - // (check file.cpp) - // try to load the archive relative to the executable - // directory first. - // If that fails, fall back to the current working - // directory (original behavior) - // if everything fails, may god help you + // boom headshot #if defined(__linux__) - // THIS CAN USE PATHHELPER.h - char exePathBuf[PATH_MAX]; - ssize_t exeLen = - readlink("/proc/self/exe", exePathBuf, sizeof(exePathBuf) - 1); - if (exeLen != -1) { - exePathBuf[exeLen] = '\0'; - std::string exePathStr(exePathBuf); - size_t pos = exePathStr.find_last_of('/'); - std::string exeDir = (pos == std::string::npos) - ? std::string(".") - : exePathStr.substr(0, pos); - std::wstring exeDirW = convStringToWstring(exeDir.c_str()); - std::wstring candidate = exeDirW + File::pathSeparator + mediapath; - if (File(candidate).exists()) { - m_mediaArchive = new ArchiveFile(File(candidate)); - } else { - m_mediaArchive = new ArchiveFile(File(mediapath)); - } + std::wstring exeDirW = PathHelper::GetExecutableDirW(); + std::wstring candidate = exeDirW + File::pathSeparator + mediapath; + if (File(candidate).exists()) { + m_mediaArchive = new ArchiveFile(File(candidate)); } else { m_mediaArchive = new ArchiveFile(File(mediapath)); } @@ -4636,6 +4616,7 @@ void CMinecraftApp::loadMediaArchive() { m_mediaArchive = new ArchiveFile(File(mediapath)); #endif } +} #if 0 std::string path = "Common\\media.arc"; HANDLE hFile = CreateFile( path.c_str(),