From 4e91d5ca4c34751c1a087b0ea290feca1905c5a5 Mon Sep 17 00:00:00 2001 From: Soggy_Pancake <54160598+Soggy-Pancake@users.noreply.github.com> Date: Sat, 14 Mar 2026 13:30:44 -0700 Subject: [PATCH] Implement ZipFile and ZipEntry --- Minecraft.Client/stubs.h | 13 +- Minecraft.World/Minecraft.World.vcxproj | 4 + .../Minecraft.World.vcxproj.filters | 12 ++ Minecraft.World/ZipEntry.cpp | 27 ++++ Minecraft.World/ZipEntry.h | 20 +++ Minecraft.World/ZipFile.cpp | 140 ++++++++++++++++++ Minecraft.World/ZipFile.h | 30 ++++ 7 files changed, 235 insertions(+), 11 deletions(-) create mode 100644 Minecraft.World/ZipEntry.cpp create mode 100644 Minecraft.World/ZipEntry.h create mode 100644 Minecraft.World/ZipFile.cpp create mode 100644 Minecraft.World/ZipFile.h diff --git a/Minecraft.Client/stubs.h b/Minecraft.Client/stubs.h index f4ae056f2..a953dd2e3 100644 --- a/Minecraft.Client/stubs.h +++ b/Minecraft.Client/stubs.h @@ -160,20 +160,11 @@ public: void dispose() {} }; -class ZipEntry -{ -}; +class ZipEntry; class InputStream; class File; -class ZipFile -{ -public: - ZipFile(File *file) {} - InputStream *getInputStream(ZipEntry *entry) { return NULL; } - ZipEntry *getEntry(const wstring& name) {return NULL;} - void close() {} -}; +class ZipFile; class ImageIO { diff --git a/Minecraft.World/Minecraft.World.vcxproj b/Minecraft.World/Minecraft.World.vcxproj index c9191573e..6e5f95bac 100644 --- a/Minecraft.World/Minecraft.World.vcxproj +++ b/Minecraft.World/Minecraft.World.vcxproj @@ -3441,6 +3441,8 @@ + + @@ -4793,6 +4795,8 @@ + + diff --git a/Minecraft.World/Minecraft.World.vcxproj.filters b/Minecraft.World/Minecraft.World.vcxproj.filters index bf872596b..d228a70fd 100644 --- a/Minecraft.World/Minecraft.World.vcxproj.filters +++ b/Minecraft.World/Minecraft.World.vcxproj.filters @@ -3197,6 +3197,12 @@ net\minecraft\world\inventory + + ConsoleJavaLibs + + + ConsoleJavaLibs + @@ -5626,5 +5632,11 @@ net\minecraft\world\item + + ConsoleJavaLibs + + + ConsoleJavaLibs + \ No newline at end of file diff --git a/Minecraft.World/ZipEntry.cpp b/Minecraft.World/ZipEntry.cpp new file mode 100644 index 000000000..7765d45c8 --- /dev/null +++ b/Minecraft.World/ZipEntry.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" +#include "ZipEntry.h" + +ZipEntry::ZipEntry(bit7z::BitArchiveItemOffset itemInfo) : _itemInfo(itemInfo) { + _name = itemInfo.name(); + _path = itemInfo.path(); +} + +DWORD ZipEntry::getSize() { + return _itemInfo.size(); +} + +bool ZipEntry::isDir() { + return _itemInfo.isDir(); +} + +std::string ZipEntry::getName() { + return _name; +} + +std::string ZipEntry::path() { + return _path; +} + +unsigned int ZipEntry::getIndex() { + return (unsigned int)_itemInfo.index(); +} \ No newline at end of file diff --git a/Minecraft.World/ZipEntry.h b/Minecraft.World/ZipEntry.h new file mode 100644 index 000000000..12eeb2023 --- /dev/null +++ b/Minecraft.World/ZipEntry.h @@ -0,0 +1,20 @@ +#pragma once +#include "../Minecraft.Client/Common/libs/bit7z/include/bitarchivereader.hpp" + +class ZipEntry { +private: + bit7z::BitArchiveItemOffset _itemInfo; + + // caching for faster load times + std::string _name; + std::string _path; + +public: + ZipEntry(bit7z::BitArchiveItemOffset itemInfo); + DWORD getSize(); + bool isDir(); + bool isFile() { return !isDir(); } + std::string getName(); + std::string path(); + unsigned int getIndex(); +}; \ No newline at end of file diff --git a/Minecraft.World/ZipFile.cpp b/Minecraft.World/ZipFile.cpp new file mode 100644 index 000000000..8e82111cc --- /dev/null +++ b/Minecraft.World/ZipFile.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include "ZipFile.h" + +// static library +bit7z::Bit7zLibrary* ZipFile::_library; + +ZipFile::ZipFile(File* file) { + if (file != NULL && file->exists()) { + open(file); + } +} + +ZipFile::ZipFile(std::string name) { + File file(convStringToWstring(name)); + if (file.exists()) { + open(&file); + } +} + +std::vector ZipFile::listFiles() { + std::vector files; + files.reserve(_fileCache.size()); + + for (auto item : _fileCache) { + files.emplace_back(item.first); + } + return files; +} + +// returns -1 if not found +std::unique_ptr ZipFile::getEntry(const std::wstring* name) { + bool path = name->find('/') != name->npos || name->find('\\') != name->npos; + std::wstring nameCopy = *name; +#ifdef _WIN64 + if (path) { // + for (int i = 0; i < nameCopy.length(); i++) + if (nameCopy[i] == L'/') + nameCopy[i] = L'\\'; + } +#endif + + auto it = _fileCache.find(nameCopy); + if (it != _fileCache.end()) { + auto entryInfo = _reader->itemAt(it->second); + return std::make_unique(entryInfo); + } + + return nullptr; +} + +std::vector ZipFile::extract(const std::wstring* name) { + auto entry = getEntry(name); + if (!entry) + return {}; + + try { + auto size = entry->getSize(); + std::vector buffer(size); + _reader->extractTo(buffer.data(), size, entry->getIndex()); + return buffer; + } + catch (exception e) { + OutputDebugString(wstringtochararray(L"Error extracting file from zip: " + *name + L'\n')); + OutputDebugString(wstringtochararray(L"Exception: " + convStringToWstring(e.what()) + L'\n')); + __debugbreak(); + return {}; + } +} + +bool ZipFile::hasFile(const std::wstring* name) { + bool path = name->find('/') != name->npos || name->find('\\') != name->npos; + std::wstring nameCopy = *name; +#ifdef _WIN64 + if (path) { // + for (int i = 0; i < nameCopy.length(); i++) + if (nameCopy[i] == L'/') + nameCopy[i] = L'\\'; + } +#endif + + return _fileCache.find(nameCopy) != _fileCache.end(); +} + +bool ZipFile::hasFile(char* str) { + try { + std::wstring wstr = convStringToWstring(std::string(str)); + return hasFile(&wstr); + } + catch (exception e) { + return false; + } +} + +InputStream* ZipFile::getInputStream(ZipEntry* entry) { + std::vector data(entry->getSize()); + _reader->extractTo(data.data(), data.size(), entry->getIndex()); + byteArray* buf = new byteArray(data.data(), data.size()); + ByteArrayInputStream* stream = new ByteArrayInputStream(*buf, 0, data.size()); + return stream; +} + +InputStream* ZipFile::getInputStream(int entryId) { + auto entry = _reader->itemAt(entryId); + std::vector data(entry.size()); + _reader->extractTo(data.data(), data.size(), entry.index()); + byteArray* buf = new byteArray(data.data(), data.size()); + ByteArrayInputStream* stream = new ByteArrayInputStream(*buf, 0, data.size()); + return stream; +} + +bool ZipFile::open(File* file) { + try { + std::wstring wpath = file->getPath(); + int charCount = WideCharToMultiByte(CP_UTF8, 0, wpath.c_str(), -1, NULL, 0, NULL, NULL); + std::string path(charCount - 1, 0); + WideCharToMultiByte(CP_UTF8, 0, wpath.c_str(), -1, &path[0], charCount, NULL, NULL); + + if (_library == nullptr) { + OutputDebugString("Initializing bit7z library"); + _library = new bit7z::Bit7zLibrary("Windows64Media\\7z.dll"); + } + + _reader = std::make_unique(*_library, path, bit7z::BitFormat::Zip); + auto _libentries = _reader->items(); + _fileCache.clear(); + + for (auto &item : _libentries) { + if (!item.isDir()) { // We never unpack directories, this also saves time comparing against them + _fileCache[item.nativePath()] = item.index(); + _fileCache[convStringToWstring(item.name())] = item.index(); + } + } + } + catch (exception e) { + OutputDebugString(wstringtochararray(L"Error opening zip file: " + file->getPath())); + OutputDebugString(wstringtochararray(L"\nException: " + convStringToWstring(e.what()))); + __debugbreak(); + } + return false; +} \ No newline at end of file diff --git a/Minecraft.World/ZipFile.h b/Minecraft.World/ZipFile.h new file mode 100644 index 000000000..a359a472d --- /dev/null +++ b/Minecraft.World/ZipFile.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include "File.h" +#include "ZipEntry.h" +#include "../Minecraft.Client/Common/libs/bit7z/include/bitarchivereader.hpp" + +class ZipFile { +public: + ZipFile(File* file); + ZipFile(std::string name); + std::vector listFiles(); + bool hasFile(const std::wstring* name); + bool hasFile(char* str); + std::unique_ptr getEntry(const std::wstring* name); + std::vector extract(const std::wstring* name); + InputStream* getInputStream(ZipEntry* entry); + InputStream* getInputStream(int entryId); + + +private: + static bit7z::Bit7zLibrary* _library; + std::unique_ptr _reader; + unordered_map _fileCache; + + + bool open(File* file); + + +}; \ No newline at end of file