diff --git a/platforms/windows/AppPlatform_windows.cpp b/platforms/windows/AppPlatform_windows.cpp index c61bc41..004fdff 100644 --- a/platforms/windows/AppPlatform_windows.cpp +++ b/platforms/windows/AppPlatform_windows.cpp @@ -103,11 +103,23 @@ void AppPlatform_windows::createUserInput() { m_UserInput.clear(); m_UserInputStatus = -1; + + switch (m_DialogType) + { + case DLG_CREATE_WORLD: + { + // some placeholder for now + m_UserInput.push_back("New World"); + m_UserInput.push_back("123456"); + m_UserInputStatus = 1; + break; + } + } } void AppPlatform_windows::showDialog(eDialogType type) { - // TODO + m_DialogType = type; } std::string AppPlatform_windows::getDateString(int time) diff --git a/platforms/windows/AppPlatform_windows.hpp b/platforms/windows/AppPlatform_windows.hpp index de55845..b4934e7 100644 --- a/platforms/windows/AppPlatform_windows.hpp +++ b/platforms/windows/AppPlatform_windows.hpp @@ -57,6 +57,8 @@ private: std::vector m_UserInput; int m_UserInputStatus = -1; + eDialogType m_DialogType; + bool m_bIsFocused = false; bool m_bGrabbedMouse = false; bool m_bActuallyGrabbedMouse = false; diff --git a/platforms/windows/SoundSystem_windows.cpp b/platforms/windows/SoundSystem_windows.cpp index f545dc5..809dabf 100644 --- a/platforms/windows/SoundSystem_windows.cpp +++ b/platforms/windows/SoundSystem_windows.cpp @@ -92,6 +92,7 @@ void SoundSystemWindows::stop(const std::string& sound) void SoundSystemWindows::playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch) { + return; //Release sounds that finished playing for (size_t i = 0; i < m_buffers.size(); i++) diff --git a/source/App/NinecraftApp.cpp b/source/App/NinecraftApp.cpp index 0e8d2a1..d6c9329 100644 --- a/source/App/NinecraftApp.cpp +++ b/source/App/NinecraftApp.cpp @@ -8,9 +8,14 @@ #include "NinecraftApp.hpp" #include "StartMenuScreen.hpp" -#include "MemoryLevelStorageSource.hpp" #include "Item.hpp" +#ifdef DEMO +#include "MemoryLevelStorageSource.hpp" +#else +#include "ExternalFileLevelStorageSource.hpp" +#endif + bool NinecraftApp::_hasInitedStatics; bool NinecraftApp::handleBack(bool b) @@ -66,7 +71,13 @@ void NinecraftApp::init() initGLStates(); Tesselator::instance.init(); Minecraft::init(); + +#ifdef DEMO m_pLevelStorageSource = new MemoryLevelStorageSource; +#else + m_pLevelStorageSource = new ExternalFileLevelStorageSource(m_externalStorageDir); +#endif + field_D9C = 0; setScreen(new StartMenuScreen); diff --git a/source/Base/Utils.cpp b/source/Base/Utils.cpp index 9abc732..2567102 100644 --- a/source/Base/Utils.cpp +++ b/source/Base/Utils.cpp @@ -16,10 +16,6 @@ #include #include -// XPL means "Cross PLatform" -#define XPL_ACCESS _access -#define XPL_MKDIR(path, mode) _mkdir(path) - // Why are we not using GetTickCount64()? It's simple -- getTimeMs has the exact same problem as using regular old GetTickCount. #pragma warning(disable : 28159) @@ -37,6 +33,70 @@ int g_TimeSecondsOnInit = 0; +DIR* opendir(const char* name) +{ + size_t len = strlen(name); + if (len == 0) + return NULL; + + char buf[1024]; + if (len >= 1024 - 5) + return NULL; + + strcpy(buf, name); + + if (name[len - 1] != '/') + strcpy(&buf[len], "/*"); + else + strcpy(&buf[len], "*"); + + DIR* pDir = (DIR*)malloc(sizeof(DIR)); + if (!pDir) + return pDir; + + memset(pDir, 0, sizeof * pDir); + + pDir->current = FindFirstFile(buf, &pDir->findData); + if (pDir->current == INVALID_HANDLE_VALUE) + { + free(pDir); + return NULL; + } + + return pDir; +} + +dirent* readdir(DIR* dir) +{ + if (dir->current == INVALID_HANDLE_VALUE) + return NULL; + + static dirent de; + + if (!dir->returnedFirstFileData) + { + dir->returnedFirstFileData = true; + } + else + { + if (!FindNextFile(dir->current, &dir->findData)) + return NULL; + } + + strcpy(de.d_name, dir->findData.cFileName); + de.d_type = (dir->findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG; + + return &de; +} + +void closedir(DIR* dir) +{ + if (dir->current != INVALID_HANDLE_VALUE) + FindClose(dir->current); + + free(dir); +} + bool createFolderIfNotExists(const char* pDir) { if (!XPL_ACCESS(pDir, 0)) @@ -45,6 +105,31 @@ bool createFolderIfNotExists(const char* pDir) return XPL_MKDIR(pDir, 0755) == 0; } +bool DeleteDirectory(const std::string& name, bool unused) +{ + DIR* dir = opendir(name.c_str()); + if (!dir) + return false; + + char buffer[1024]; + + while (true) + { + dirent* de = readdir(dir); + if (!de) + break; + + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + snprintf(buffer, sizeof buffer, "%s/%s", name.c_str(), de->d_name); + remove(buffer); + } + + closedir(dir); + return remove(name.c_str()) == 0; +} + const char* GetTerrainName() { return "terrain.png"; diff --git a/source/Base/Utils.hpp b/source/Base/Utils.hpp index 6b33a5c..cae19d3 100644 --- a/source/Base/Utils.hpp +++ b/source/Base/Utils.hpp @@ -15,16 +15,51 @@ #include #include +#include + #ifdef _WIN32 // @HACK: Include WinSock2.h also #include #include #include +#include +#include + +// XPL means "Cross PLatform" +#define XPL_ACCESS _access +#define XPL_MKDIR(path, mode) _mkdir(path) + +// Bare bones non conforming implementation, but good enough +struct dirent +{ + int d_type; + char d_name[_MAX_PATH + 1]; +}; + +struct DIR +{ + HANDLE current; + WIN32_FIND_DATA findData; + bool returnedFirstFileData; +}; + +#define DT_UNKNOWN (0) +#define DT_DIR (4) +#define DT_REG (8) + +DIR* opendir(const char* name); +dirent* readdir(DIR* dir); +void closedir(DIR* dir); #else #include +#include + + // XPL means "Cross PLatform" +#define XPL_ACCESS access +#define XPL_MKDIR(path, mode) mkdir(path, mode) #endif @@ -505,6 +540,7 @@ constexpr float Lerp(float a, float b, float progress) } bool createFolderIfNotExists(const char* pDir); +bool DeleteDirectory(const std::string& name, bool unused); // things that we added: #ifndef ORIGINAL_CODE diff --git a/source/GUI/Screen/SelectWorldScreen.cpp b/source/GUI/Screen/SelectWorldScreen.cpp index 21757f6..dca7304 100644 --- a/source/GUI/Screen/SelectWorldScreen.cpp +++ b/source/GUI/Screen/SelectWorldScreen.cpp @@ -126,7 +126,10 @@ void SelectWorldScreen::tick() m_pMinecraft->hostMultiplayer(); m_pMinecraft->setScreen(new ProgressScreen); + // @BUG: Use of deallocated memory. SetScreen frees us +#ifdef ORIGINAL_CODE field_130 = 0; +#endif return; } diff --git a/source/GUI/Screen/StartMenuScreen.cpp b/source/GUI/Screen/StartMenuScreen.cpp index e25c0a8..d95218e 100644 --- a/source/GUI/Screen/StartMenuScreen.cpp +++ b/source/GUI/Screen/StartMenuScreen.cpp @@ -48,16 +48,8 @@ void StartMenuScreen::buttonClicked(Button* pButton) { if (pButton->field_30 == m_startButton.field_30) { -#if defined(DEMO) || !defined(ORIGINAL_CODE) - -# ifdef DEMO -# define DEMO_SEED int(getEpochTimeS()) -# else - // 1942892620 = long(12345678901324) -# define DEMO_SEED 123456 -# endif - - m_pMinecraft->selectLevel("_DemoLevel", "_DemoLevel", DEMO_SEED); +#if defined(DEMO) + m_pMinecraft->selectLevel("_DemoLevel", "_DemoLevel", int(getEpochTimeS())); m_pMinecraft->hostMultiplayer(); m_pMinecraft->setScreen(new ProgressScreen); #else diff --git a/source/World/Level.cpp b/source/World/Level.cpp index 7e8f91e..a17127d 100644 --- a/source/World/Level.cpp +++ b/source/World/Level.cpp @@ -45,6 +45,7 @@ void Level::_init(const std::string& str, TLong seed, int x, Dimension* pDimens field_B0C = pData == 0; + // @BUG: leaking a Dimension*? if (pDimension) m_pDimension = pDimension; else diff --git a/source/World/LevelData.hpp b/source/World/LevelData.hpp index 4b382ad..295a9e5 100644 --- a/source/World/LevelData.hpp +++ b/source/World/LevelData.hpp @@ -55,5 +55,10 @@ struct LevelData { return field_20; } + + void setLevelName(const std::string& name) + { + field_78 = name; + } }; diff --git a/source/World/RegionFile.cpp b/source/World/RegionFile.cpp index 72c169d..dae9203 100644 --- a/source/World/RegionFile.cpp +++ b/source/World/RegionFile.cpp @@ -15,7 +15,7 @@ RegionFile::RegionFile(const std::string fileName) field_20 = new int[1024]; field_24 = new int[1024]; - memset(field_20, 0, 1024 * sizeof(int)); + memset(field_24, 0, 1024 * sizeof(int)); } RegionFile::~RegionFile() @@ -48,11 +48,14 @@ bool RegionFile::open() for (int i = 0; i < 1024; i++) { - if (field_20[i]) + int v13 = this->field_20[i]; + if (v13) { - for (int j = 0; j < uint8_t(field_20[i]); j++) + int v12 = v13 >> 8; + int v11 = uint8_t(v13); + for (int j = 0; j < v11; ++j) { - field_28[j + (field_20[i] >> 8)] = false; + field_28[j + v12] = false; } } } @@ -66,6 +69,8 @@ bool RegionFile::open() WRITE(field_20, sizeof(int), 1024, m_pFile); field_28[0] = false; + + return true; } bool RegionFile::readChunk(int x, int z, RakNet::BitStream** pBitStream) @@ -74,8 +79,11 @@ bool RegionFile::readChunk(int x, int z, RakNet::BitStream** pBitStream) if (!idx) return false; + int thing = (idx >> 8); + int offset = (idx & 0xFF); + int length = 0; - fseek(m_pFile, (idx >> 8) * SECTOR_BYTES, SEEK_SET); + fseek(m_pFile, thing * SECTOR_BYTES, SEEK_SET); fread(&length, sizeof(int), 1, m_pFile); assert(length < ((offset & 0xff) * SECTOR_BYTES)); @@ -104,7 +112,7 @@ bool RegionFile::writeChunk(int x, int z, RakNet::BitStream& bitStream) { int length = bitStream.GetNumberOfBytesUsed(); int field20i = field_20[32 * z + x]; - int lowerIndex = (length + 4) / SECTOR_BYTES; + int lowerIndex = (length + 4) / SECTOR_BYTES + 1; if (lowerIndex > 256) return false; diff --git a/source/World/Storage/ExternalFileLevelStorage.cpp b/source/World/Storage/ExternalFileLevelStorage.cpp index abf0c6c..bd1ecc3 100644 --- a/source/World/Storage/ExternalFileLevelStorage.cpp +++ b/source/World/Storage/ExternalFileLevelStorage.cpp @@ -1,3 +1,11 @@ +/******************************************************************** + Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2023 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ + #include "ExternalFileLevelStorage.hpp" #include "Level.hpp" #include "GetTime.h" @@ -237,7 +245,7 @@ bool ExternalFileLevelStorage::readLevelData(const std::string& path, LevelData* if (fread(data, sizeof(uint8_t), length, pFile) != length) { - SAFE_DELETE(data); + SAFE_DELETE_ARRAY(data); goto _cleanup; } diff --git a/source/World/Storage/ExternalFileLevelStorage.hpp b/source/World/Storage/ExternalFileLevelStorage.hpp index 95664d6..1edaffd 100644 --- a/source/World/Storage/ExternalFileLevelStorage.hpp +++ b/source/World/Storage/ExternalFileLevelStorage.hpp @@ -1,3 +1,11 @@ +/******************************************************************** + Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2023 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ + #pragma once #include diff --git a/source/World/Storage/ExternalFileLevelStorageSource.cpp b/source/World/Storage/ExternalFileLevelStorageSource.cpp new file mode 100644 index 0000000..997c177 --- /dev/null +++ b/source/World/Storage/ExternalFileLevelStorageSource.cpp @@ -0,0 +1,164 @@ +/******************************************************************** + Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2023 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ + +#include "ExternalFileLevelStorageSource.hpp" +#include "ExternalFileLevelStorage.hpp" +#include "Util.hpp" + +ExternalFileLevelStorageSource::ExternalFileLevelStorageSource(const std::string& path) +{ + m_worldsPath = path; + + m_worldsPath += "/games"; + if (createFolderIfNotExists(m_worldsPath.c_str())) + { + m_worldsPath += "/com.mojang"; + if (createFolderIfNotExists(m_worldsPath.c_str())) + { + m_worldsPath += "/minecraftWorlds"; + if (createFolderIfNotExists(m_worldsPath.c_str())) + { + std::vector vls; + getLevelList(vls); + } + } + } + + m_worldsPath = path + "/games" + "/com.mojang" + "/minecraftWorlds"; +} + +std::string ExternalFileLevelStorageSource::getName() +{ + return "External File Level Storage"; +} + +LevelStorage* ExternalFileLevelStorageSource::selectLevel(const std::string& name, bool b) +{ + return new ExternalFileLevelStorage(name, m_worldsPath + "/" + name); +} + +void ExternalFileLevelStorageSource::getLevelList(std::vector& vls) +{ + DIR* dir = opendir(m_worldsPath.c_str()); + if (!dir) + return; + + while (true) + { + dirent* de = readdir(dir); + if (!de) + break; + + LogMsg("Entry: %s", de->d_name); + + if (de->d_type == DT_DIR) + { + addLevelSummaryIfExists(vls, de->d_name); + } + } + + closedir(dir); +} + +void ExternalFileLevelStorageSource::clearAll() +{ +} + +int ExternalFileLevelStorageSource::getDataTagFor(const std::string& str) +{ + return 0; +} + +bool ExternalFileLevelStorageSource::isNewLevelIdAcceptable(const std::string& levelID) +{ + return true; +} + +static char g_EFLSSFilterArray[] = { '/','\n','\r','\x09','\0','\xC','`','?','*','\\','<','>','|','"',':' }; + +void ExternalFileLevelStorageSource::deleteLevel(const std::string& levelName) +{ + std::stringstream ss; + ss << m_worldsPath << "/" << levelName; + std::string path = ss.str(); + + if (DeleteDirectory(path, true)) + return; + + remove((path + "/chunks.dat").c_str()); + remove((path + "/player.dat").c_str()); + remove((path + "/level.dat").c_str()); +} + +void ExternalFileLevelStorageSource::renameLevel(const std::string& oldName, const std::string& newName) +{ + int accessResult = XPL_ACCESS((m_worldsPath + "/" + oldName).c_str(), 0); + if (accessResult) + return; + + std::string levelName = Util::stringTrim(newName); + for (int i = 0; i < sizeof(g_EFLSSFilterArray); i++) + { + std::string str; + str.push_back(g_EFLSSFilterArray[i]); + Util::stringReplace(levelName, str, ""); + } + + std::vector vls; + getLevelList(vls); + + std::set maps; + + for (const auto& ls : vls) + maps.insert(ls.field_0); + + std::string levelUniqueName = levelName; + while (maps.find(levelUniqueName) != maps.end()) + levelUniqueName += "-"; + + int renameResult = rename((m_worldsPath + "/" + oldName).c_str(), (m_worldsPath + "/" + levelUniqueName).c_str()); + if (renameResult != 0) + levelUniqueName = oldName; + + LevelData ld; + ExternalFileLevelStorage::readLevelData(m_worldsPath + "/" + levelUniqueName + "/" + "level.dat", &ld); + ld.setLevelName(levelName); + ExternalFileLevelStorage::writeLevelData(m_worldsPath + "/" + levelUniqueName + "/" + "level.dat", &ld); +} + +bool ExternalFileLevelStorageSource::isConvertible(const std::string&) +{ + return false; +} + +bool ExternalFileLevelStorageSource::requiresConversion(const std::string&) +{ + return false; +} + +int ExternalFileLevelStorageSource::convertLevel(const std::string&, ProgressListener*) +{ + return 0; +} + +void ExternalFileLevelStorageSource::addLevelSummaryIfExists(std::vector& vls, const char* name) +{ + std::string levelDat = m_worldsPath + "/" + name + "/" + "level.dat"; + + LevelData ld; + + if (!ExternalFileLevelStorage::readLevelData(levelDat, &ld)) + return; + + LevelSummary ls; + ls.field_0 = name; + ls.field_18 = ld.field_78; + ls.field_30 = ld.field_14; + ls.field_34 = ld.field_18; + vls.push_back(ls); +} diff --git a/source/World/Storage/ExternalFileLevelStorageSource.hpp b/source/World/Storage/ExternalFileLevelStorageSource.hpp new file mode 100644 index 0000000..e51f483 --- /dev/null +++ b/source/World/Storage/ExternalFileLevelStorageSource.hpp @@ -0,0 +1,40 @@ +/******************************************************************** + Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2023 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ + +#pragma once + +#include +#include +#include "LevelStorageSource.hpp" + +#ifndef DEMO + +class ExternalFileLevelStorageSource : public LevelStorageSource +{ +public: + ExternalFileLevelStorageSource(const std::string& path); + + std::string getName() override; + LevelStorage* selectLevel(const std::string&, bool) override; + void getLevelList(std::vector&); + void clearAll() override; + int getDataTagFor(const std::string&) override; + bool isNewLevelIdAcceptable(const std::string&) override; + void deleteLevel(const std::string&) override; + void renameLevel(const std::string&, const std::string&) override; + bool isConvertible(const std::string&) override; + bool requiresConversion(const std::string&) override; + int convertLevel(const std::string&, ProgressListener*) override; + + void addLevelSummaryIfExists(std::vector& vls, const char* name); + +public: + std::string m_worldsPath; +}; + +#endif diff --git a/source/World/Storage/LevelStorage.cpp b/source/World/Storage/LevelStorage.cpp index 5f105ff..d3dc3b1 100644 --- a/source/World/Storage/LevelStorage.cpp +++ b/source/World/Storage/LevelStorage.cpp @@ -14,6 +14,8 @@ LevelStorage::~LevelStorage() void LevelStorage::saveLevelData(LevelData* levelData) { + std::vector nothing; + saveLevelData(levelData, nothing); } void LevelStorage::savePlayerData(LevelData* levelData, std::vector& players) diff --git a/source/World/Storage/LevelStorageSource.hpp b/source/World/Storage/LevelStorageSource.hpp index a327515..4e0f263 100644 --- a/source/World/Storage/LevelStorageSource.hpp +++ b/source/World/Storage/LevelStorageSource.hpp @@ -48,6 +48,6 @@ public: virtual void renameLevel(const std::string&, const std::string&) = 0; virtual bool isConvertible(const std::string&) = 0; virtual bool requiresConversion(const std::string&) = 0; - virtual void convertLevel(const std::string&, ProgressListener*) = 0; + virtual int convertLevel(const std::string&, ProgressListener*) = 0; }; diff --git a/source/World/Storage/MemoryLevelStorageSource.cpp b/source/World/Storage/MemoryLevelStorageSource.cpp index 25021d5..9a9959b 100644 --- a/source/World/Storage/MemoryLevelStorageSource.cpp +++ b/source/World/Storage/MemoryLevelStorageSource.cpp @@ -9,6 +9,8 @@ #include "MemoryLevelStorage.hpp" #include "MemoryLevelStorageSource.hpp" +#ifdef DEMO + std::string MemoryLevelStorageSource::getName() { return "Memory Storage"; @@ -52,7 +54,9 @@ bool MemoryLevelStorageSource::requiresConversion(const std::string& x) return false; } -void MemoryLevelStorageSource::convertLevel(const std::string& x, ProgressListener* y) +int MemoryLevelStorageSource::convertLevel(const std::string& x, ProgressListener* y) { - + return 0; } + +#endif diff --git a/source/World/Storage/MemoryLevelStorageSource.hpp b/source/World/Storage/MemoryLevelStorageSource.hpp index 4833358..ce45a61 100644 --- a/source/World/Storage/MemoryLevelStorageSource.hpp +++ b/source/World/Storage/MemoryLevelStorageSource.hpp @@ -10,6 +10,7 @@ #include "LevelStorageSource.hpp" +#ifdef DEMO class MemoryLevelStorageSource : public LevelStorageSource { std::string getName() override; @@ -21,6 +22,6 @@ class MemoryLevelStorageSource : public LevelStorageSource void renameLevel(const std::string&, const std::string&) override; bool isConvertible(const std::string&) override; bool requiresConversion(const std::string&) override; - void convertLevel(const std::string&, ProgressListener*) override; + int convertLevel(const std::string&, ProgressListener*) override; }; - +#endif diff --git a/thirdparty/direntm.h b/thirdparty/direntm.h new file mode 100644 index 0000000..2d61617 --- /dev/null +++ b/thirdparty/direntm.h @@ -0,0 +1,585 @@ +/* +MIT License +Copyright (c) 2019 win32ports +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once + +#ifndef __DIRENT_H_9DE6B42C_8D0C_4D31_A8EF_8E4C30E6C46A__ +#define __DIRENT_H_9DE6B42C_8D0C_4D31_A8EF_8E4C30E6C46A__ + +#ifndef _WIN32 + +//#pragma message("this dirent.h implementation is for Windows only!") -- it's going to just use the regular dirent +#include + +#else /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include + +#include + +#include + +#ifdef _MSC_VER +#pragma comment(lib, "Shlwapi.lib") +#endif + +#ifndef NAME_MAX +#define NAME_MAX 260 +#endif /* NAME_MAX */ + +#ifndef DT_UNKNOWN +#define DT_UNKNOWN 0 +#endif /* DT_UNKNOWN */ + +#ifndef DT_FIFO +#define DT_FIFO 1 +#endif /* DT_FIFO */ + +#ifndef DT_CHR +#define DT_CHR 2 +#endif /* DT_CHR */ + +#ifndef DT_DIR +#define DT_DIR 4 +#endif /* DT_DIR */ + +#ifndef DT_BLK +#define DT_BLK 6 +#endif /* DT_BLK */ + +#ifndef DT_REG +#define DT_REG 8 +#endif /* DT_REF */ + +#ifndef DT_LNK +#define DT_LNK 10 +#endif /* DT_LNK */ + +#ifndef DT_SOCK +#define DT_SOCK 12 +#endif /* DT_SOCK */ + +#ifndef DT_WHT +#define DT_WHT 14 +#endif /* DT_WHT */ + +#ifndef _DIRENT_HAVE_D_NAMLEN +#define _DIRENT_HAVE_D_NAMLEN 1 +#endif /* _DIRENT_HAVE_D_NAMLEN */ + +#ifndef _DIRENT_HAVE_D_RECLEN +#define _DIRENT_HAVE_D_RECLEN 1 +#endif /* _DIRENT_HAVE_D_RECLEN */ + +#ifndef _DIRENT_HAVE_D_OFF +#define _DIRENT_HAVE_D_OFF 1 +#endif /* _DIRENT_HAVE_D_OFF */ + +#ifndef _DIRENT_HAVE_D_TYPE +#define _DIRENT_HAVE_D_TYPE 1 +#endif /* _DIRENT_HAVE_D_TYPE */ + +#ifndef NTFS_MAX_PATH +#define NTFS_MAX_PATH 32768 +#endif /* NTFS_MAX_PATH */ + +#ifndef FSCTL_GET_REPARSE_POINT +#define FSCTL_GET_REPARSE_POINT 0x900a8 +#endif /* FSCTL_GET_REPARSE_POINT */ + +#ifndef FILE_NAME_NORMALIZED +#define FILE_NAME_NORMALIZED 0 +#endif /* FILE_NAME_NORMALIZED */ + +typedef void* DIR; + +typedef struct ino_t +{ + unsigned long long serial; + unsigned char fileid[16]; +} __ino_t; + +struct dirent +{ + __ino_t d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_namelen; + unsigned char d_type; + char d_name[NAME_MAX]; +}; + +struct __dir +{ + struct dirent* entries; + intptr_t fd; + long int count; + long int index; +}; + +static int closedir(DIR* dirp) +{ + struct __dir* data = NULL; + if (!dirp) { + errno = EBADF; + return -1; + } + data = (struct __dir*) dirp; + CloseHandle((HANDLE)data->fd); + free(data->entries); + free(data); + return 0; +} + +static void __seterrno(int value) +{ +#ifdef _MSC_VER + _set_errno(value); +#else /* _MSC_VER */ + errno = value; +#endif /* _MSC_VER */ +} + +static int __islink(const wchar_t * name, char * buffer) +{ + DWORD io_result = 0; + DWORD bytes_returned = 0; + HANDLE hFile = CreateFileW(name, 0, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + + io_result = DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, + buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes_returned, NULL); + + CloseHandle(hFile); + + if (io_result == 0) + return 0; + + return ((REPARSE_GUID_DATA_BUFFER*)buffer)->ReparseTag == IO_REPARSE_TAG_SYMLINK; +} + +#pragma pack(push, 1) + +typedef struct dirent_FILE_ID_128 +{ + BYTE Identifier[16]; +} +dirent_FILE_ID_128; + +typedef struct _dirent_FILE_ID_INFO +{ + ULONGLONG VolumeSerialNumber; + dirent_FILE_ID_128 FileId; +} +dirent_FILE_ID_INFO; + +#pragma pack(pop) + +typedef enum dirent_FILE_INFO_BY_HANDLE_CLASS +{ dirent_FileIdInfo = 18 } +dirent_FILE_INFO_BY_HANDLE_CLASS; + +static __ino_t __inode(const wchar_t* name) +{ + __ino_t value = { 0 }; + BOOL result; + dirent_FILE_ID_INFO fileid; + BY_HANDLE_FILE_INFORMATION info; + typedef BOOL (__stdcall* pfnGetFileInformationByHandleEx)(HANDLE hFile, + dirent_FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + LPVOID lpFileInformation, DWORD dwBufferSize); + + HANDLE hKernel32 = GetModuleHandleW(L"kernel32.dll"); + if (!hKernel32) + return value; + + pfnGetFileInformationByHandleEx fnGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx) GetProcAddress(hKernel32, "GetFileInformationByHandleEx"); + if (!fnGetFileInformationByHandleEx) + return value; + + HANDLE hFile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (hFile == INVALID_HANDLE_VALUE) + return value; + + result = fnGetFileInformationByHandleEx(hFile, dirent_FileIdInfo, &fileid, sizeof(fileid)); + if (result) + { + value.serial = fileid.VolumeSerialNumber; + memcpy(value.fileid, fileid.FileId.Identifier, 16); + } + else + { + result = GetFileInformationByHandle(hFile, &info); + if(result) + { + value.serial = info.dwVolumeSerialNumber; + memcpy(value.fileid + 8, &info.nFileIndexHigh, 4); + memcpy(value.fileid + 12, &info.nFileIndexLow, 4); + } + } + CloseHandle(hFile); + return value; +} + +static DIR* __internal_opendir(wchar_t* wname, int size) +{ + struct __dir* data = NULL; + struct dirent *tmp_entries = NULL; + static char default_char = '?'; + static wchar_t* prefix = L"\\\\?\\"; + static wchar_t* suffix = L"\\*.*"; + static int extra_prefix = 4; /* use prefix "\\?\" to handle long file names */ + static int extra_suffix = 4; /* use suffix "\*.*" to find everything */ + WIN32_FIND_DATAW w32fd = { 0 }; + HANDLE hFindFile = INVALID_HANDLE_VALUE; + static int grow_factor = 2; + char* buffer = NULL; + + BOOL relative = PathIsRelativeW(wname + extra_prefix); + + memcpy(wname + size - 1, suffix, sizeof(wchar_t) * extra_suffix); + wname[size + extra_suffix - 1] = 0; + + if (relative) { + wname += extra_prefix; + size -= extra_prefix; + } + hFindFile = FindFirstFileW(wname, &w32fd); + if (INVALID_HANDLE_VALUE == hFindFile) + { + __seterrno(ENOENT); + return NULL; + } + + data = (struct __dir*) malloc(sizeof(struct __dir)); + if (!data) + goto out_of_memory; + wname[size - 1] = 0; + data->fd = (intptr_t)CreateFileW(wname, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + wname[size - 1] = L'\\'; + data->count = 16; + data->index = 0; + data->entries = (struct dirent*) malloc(sizeof(struct dirent) * data->count); + if (!data->entries) + goto out_of_memory; + buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!buffer) + goto out_of_memory; + do + { + WideCharToMultiByte(CP_UTF8, 0, w32fd.cFileName, -1, data->entries[data->index].d_name, NAME_MAX, &default_char, NULL); + + memcpy(wname + size, w32fd.cFileName, sizeof(wchar_t) * NAME_MAX); + + if (((w32fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT) && __islink(wname, buffer)) + data->entries[data->index].d_type = DT_LNK; + else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) == FILE_ATTRIBUTE_DEVICE) + data->entries[data->index].d_type = DT_CHR; + else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) + data->entries[data->index].d_type = DT_DIR; + else + data->entries[data->index].d_type = DT_REG; + + data->entries[data->index].d_ino = __inode(wname); + data->entries[data->index].d_reclen = sizeof(struct dirent); + data->entries[data->index].d_namelen = (unsigned char)wcslen(w32fd.cFileName); + data->entries[data->index].d_off = 0; + + if (++data->index == data->count) { + tmp_entries = (struct dirent*) realloc(data->entries, sizeof(struct dirent) * data->count * grow_factor); + if (!tmp_entries) + goto out_of_memory; + data->entries = tmp_entries; + data->count *= grow_factor; + } + } + while (FindNextFileW(hFindFile, &w32fd) != 0); + + free(buffer); + FindClose(hFindFile); + + data->count = data->index; + data->index = 0; + return (DIR*)data; +out_of_memory: + if (data) + { + if (INVALID_HANDLE_VALUE != (HANDLE)data->fd) + CloseHandle((HANDLE)data->fd); + free(data->entries); + } + free(buffer); + free(data); + if (INVALID_HANDLE_VALUE != hFindFile) + FindClose(hFindFile); + __seterrno(ENOMEM); + return NULL; +} + +static wchar_t* __get_buffer() +{ + wchar_t* name = malloc(sizeof(wchar_t) * (NTFS_MAX_PATH + NAME_MAX + 8)); + if (name) + memcpy(name, L"\\\\?\\", sizeof(wchar_t) * 4); + return name; +} + +static DIR* opendir(const char* name) +{ + DIR* dirp = NULL; + wchar_t* wname = __get_buffer(); + int size = 0; + if (!wname) + { + errno = ENOMEM; + return NULL; + } + size = MultiByteToWideChar(CP_UTF8, 0, name, -1, wname + 4, NTFS_MAX_PATH); + if (0 == size) + { + free(wname); + return NULL; + } + dirp = __internal_opendir(wname, size + 4); + free(wname); + return dirp; +} + +static DIR* _wopendir(const wchar_t* name) +{ + DIR* dirp = NULL; + wchar_t* wname = __get_buffer(); + int size = 0; + if (!wname) + { + errno = ENOMEM; + return NULL; + } + size = (int)wcslen(name); + if (size > NTFS_MAX_PATH) + { + free(wname); + return NULL; + } + memcpy(wname + 4, name, sizeof(wchar_t) * (size + 1)); + dirp = __internal_opendir(wname, size + 5); + free(wname); + return dirp; +} + +static DIR* fdopendir(intptr_t fd) +{ + DIR* dirp = NULL; + wchar_t* wname = __get_buffer(); + typedef DWORD (__stdcall * pfnGetFinalPathNameByHandleW)( + HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); + + HANDLE hKernel32 = GetModuleHandleW(L"kernel32.dll"); + if (!hKernel32) + { + errno = EINVAL; + return NULL; + } + + pfnGetFinalPathNameByHandleW fnGetFinalPathNameByHandleW = (pfnGetFinalPathNameByHandleW) GetProcAddress(hKernel32, "GetFinalPathNameByHandleW"); + if (!fnGetFinalPathNameByHandleW) + { + errno = EINVAL; + return NULL; + } + + int size = 0; + if (!wname) + { + errno = ENOMEM; + return NULL; + } + size = fnGetFinalPathNameByHandleW((HANDLE) fd, wname + 4, NTFS_MAX_PATH, FILE_NAME_NORMALIZED); + if (0 == size) + { + free(wname); + errno = ENOTDIR; + return NULL; + } + dirp = __internal_opendir(wname, size + 5); + free(wname); + return dirp; +} + +static struct dirent* readdir(DIR* dirp) +{ + struct __dir* data = (struct __dir*) dirp; + if (!data) { + errno = EBADF; + return NULL; + } + if (data->index < data->count) + { + return &data->entries[data->index++]; + } + return NULL; +} + +static int readdir_r(DIR* dirp, struct dirent* entry, struct dirent**result) +{ + struct __dir* data = (struct __dir*) dirp; + if (!data) { + return EBADF; + } + if (data->index < data->count) + { + if (entry) + memcpy(entry, &data->entries[data->index++], sizeof(struct dirent)); + if (result) + *result = entry; + } + else if (result) + *result = NULL; + return 0; +} + +static void seekdir(DIR* dirp, long int offset) +{ + if (dirp) + { + struct __dir* data = (struct __dir*) dirp; + data->index = (offset < data->count) ? offset : data->index; + } +} + +static void rewinddir(DIR* dirp) +{ + seekdir(dirp, 0); +} + +static long int telldir(DIR* dirp) +{ + if (!dirp) { + errno = EBADF; + return -1; + } + return ((struct __dir*)dirp)->count; +} + +static intptr_t dirfd(DIR * dirp) +{ + if (!dirp) { + errno = EINVAL; + return -1; + } + return ((struct __dir*)dirp)->fd; +} + +static int scandir(const char* dirp, struct dirent*** namelist, + int (*filter)(const struct dirent*), + int (*compar)(const struct dirent**, const struct dirent**)) +{ + struct dirent ** entries = NULL, ** tmp_entries = NULL; + long int i = 0, index = 0, count = 16; + DIR * d = opendir(dirp); + struct __dir* data = (struct __dir*) d; + if (!data) { + closedir(d); + __seterrno(ENOENT); + return -1; + } + entries = (struct dirent**) malloc(sizeof(struct dirent*) * count); + if (!entries) + { + closedir(d); + __seterrno(ENOMEM); + return -1; + } + for (i = 0; i < data->count; ++i) + { + if (!filter || filter(&data->entries[i])) + { + entries[index] = (struct dirent*) malloc(sizeof(struct dirent)); + if (!entries[index]) + { + closedir(d); + for (i = 0; i < index; ++i) + free(entries[index]); + free(entries); + __seterrno(ENOMEM); + return -1; + } + memcpy(entries[index], &data->entries[i], sizeof(struct dirent)); + if (++index == count) + { + tmp_entries = (struct dirent**)realloc(entries, sizeof(struct dirent*) * count * 2); + if (!tmp_entries) + { + closedir(d); + for (i = 0; i < index; ++i) + free(entries[index - 1]); + free(entries); + __seterrno(ENOMEM); + return -1; + } + entries = tmp_entries; + count *= 2; + } + } + } + qsort(entries, index, sizeof(struct dirent*), compar); + entries[index] = NULL; + if (namelist) + *namelist = entries; + closedir(d); + return 0; +} + +int alphasort(const void* a, const void* b) +{ + struct dirent** dira = (struct dirent**)a, **dirb = (struct dirent**)b; + if (!dira || !dirb) + return 0; + return strcoll((*dira)->d_name, (*dirb)->d_name); +} + +static int __strverscmp(const char* s1, const char* s2) +{ + return alphasort(s1, s2); +} + +int versionsort(const void* a, const void* b) +{ + struct dirent** dira = (struct dirent**)a, ** dirb = (struct dirent**)b; + if (!dira || !dirb) + return 0; + return __strverscmp((*dira)->d_name, (*dirb)->d_name); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _WIN32 */ + +#endif /* __DIRENT_H_9DE6B42C_8D0C_4D31_A8EF_8E4C30E6C46A__ */ \ No newline at end of file diff --git a/windows_vs/minecraftcpp.vcxproj b/windows_vs/minecraftcpp.vcxproj index 48ee41e..e6d0a37 100644 --- a/windows_vs/minecraftcpp.vcxproj +++ b/windows_vs/minecraftcpp.vcxproj @@ -155,6 +155,7 @@ + @@ -474,6 +475,7 @@ + diff --git a/windows_vs/minecraftcpp.vcxproj.filters b/windows_vs/minecraftcpp.vcxproj.filters index 5ad96c3..e8d3fcf 100644 --- a/windows_vs/minecraftcpp.vcxproj.filters +++ b/windows_vs/minecraftcpp.vcxproj.filters @@ -1065,6 +1065,9 @@ Source Files\World\Storage + + Source Files\World\Storage + @@ -1934,9 +1937,12 @@ Header Files\World - + Header Files + + Header Files\World\Storage +