Modernise portable file and timing utilities

This commit is contained in:
notmatthewbeshay 2026-03-09 23:26:18 +11:00
parent ca48a01a81
commit e1a66b0ad0
7 changed files with 303 additions and 217 deletions

View file

@ -1,6 +1,7 @@
#include "../Platform/stdafx.h"
#include "../../Minecraft.World/Util/StringHelpers.h"
#include "../../Minecraft.World/Util/PortableFileIO.h"
#include "../../Minecraft.World/IO/Streams/Compression.h"
#include "ArchiveFile.h"
@ -120,66 +121,18 @@ byteArray ArchiveFile::getFile(const std::wstring &filename)
memcpy( out.data, m_cachedData + data->ptr, data->filesize );
#else
const unsigned int fileSize = static_cast<unsigned int>(data->filesize);
PBYTE pbData = new BYTE[fileSize == 0 ? 1 : fileSize];
out = byteArray(pbData, fileSize);
const PortableFileIO::BinaryReadResult readResult = PortableFileIO::ReadBinaryFileSegment(
m_sourcefile.getPath(),
static_cast<std::size_t>(data->ptr),
out.data,
static_cast<std::size_t>(data->filesize));
#if defined(_UNICODE) && !defined(__linux__)
HANDLE hfile = CreateFile( m_sourcefile.getPath().c_str(),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
#else
app.DebugPrintf("Createfile archive\n");
HANDLE hfile = CreateFile( wstringtofilename(m_sourcefile.getPath()),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
#endif
if (hfile != INVALID_HANDLE_VALUE)
if (readResult.status != PortableFileIO::BinaryReadStatus::ok)
{
app.DebugPrintf("hfile ok\n");
DWORD ok = SetFilePointer( hfile,
data->ptr,
NULL,
FILE_BEGIN
);
if (ok != INVALID_SET_FILE_POINTER)
{
PBYTE pbData = new BYTE[ data->filesize ];
DWORD bytesRead = -1;
BOOL bSuccess = ReadFile( hfile,
(LPVOID) pbData,
data->filesize,
&bytesRead,
NULL
);
if(bSuccess==FALSE)
{
app.FatalLoadError();
}
assert(bytesRead == data->filesize);
out = byteArray(pbData, data->filesize);
}
else
{
app.FatalLoadError();
}
CloseHandle(hfile);
}
else
{
app.DebugPrintf("bad hfile\n");
app.DebugPrintf("Failed to read archive file segment\n");
app.FatalLoadError();
}
#endif

View file

@ -3,6 +3,11 @@
#include "../../Level/Storage/McRegionLevelStorageSource.h"
#include "File.h"
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
#include <chrono>
#include <filesystem>
#endif
#ifdef __PS3__
#include <cell/cell_fs.h>
#endif
@ -17,6 +22,34 @@ const std::wstring File::pathRoot = L"GAME:"; // Path root after pathSeparator h
const std::wstring File::pathRoot = L""; // Path root after pathSeparator has been removed
#endif
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
namespace
{
namespace fs = std::filesystem;
fs::path ToFilesystemPath(const std::wstring& path)
{
const std::string nativePath = wstringtofilename(path);
return fs::u8path(nativePath);
}
std::wstring ToFilename(const fs::path& path)
{
const std::string filename = path.filename().u8string();
return filenametowstring(filename.c_str());
}
__int64 ToEpochMilliseconds(const fs::file_time_type& fileTime)
{
using namespace std::chrono;
const auto systemTime = time_point_cast<milliseconds>(
fileTime - fs::file_time_type::clock::now() + system_clock::now());
return static_cast<__int64>(systemTime.time_since_epoch().count());
}
}
#endif
//Creates a new File instance from a parent abstract pathname and a child pathname string.
File::File( const File &parent, const std::wstring& child )
{
@ -97,7 +130,36 @@ this->parent = NULL;
//true if and only if the file or directory is successfully deleted; false otherwise
bool File::_delete()
{
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
const bool result = fs::remove(ToFilesystemPath(getPath()), error);
#if defined _UNICODE
if( !result || error )
{
#ifndef _CONTENT_PACKAGE
printf( "File::_delete - Error code %d (%#0.8X)\n", error.value(), error.value() );
#endif
return false;
}
return true;
#elif defined(__linux__)
if( !result || error )
{
printf("Unable to delete file :(");
return false;
}
return true;
#else
if( !result || error )
{
#ifndef _CONTENT_PACKAGE
printf( "File::_delete - Error code %d (%#0.8X)\n", error.value(), error.value() );
#endif
return false;
}
return true;
#endif
#elif defined _UNICODE
BOOL result = DeleteFile( getPath().c_str() );
#elif defined(__linux__)
// FIXME
@ -126,7 +188,10 @@ bool File::_delete()
//true if and only if the directory was created; false otherwise
bool File::mkdir() const
{
#if defined (_UNICODE)
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
return fs::create_directory(ToFilesystemPath(getPath()), error);
#elif defined (_UNICODE)
return CreateDirectory( getPath().c_str(), NULL) != 0;
#elif defined(__linux__)
return ::mkdir(wstringtofilename(getPath()), 0777) == 0;
@ -157,6 +222,22 @@ bool File::mkdir() const
//
bool File::mkdirs() const
{
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
const fs::path path = ToFilesystemPath(getPath());
if( fs::exists(path, error) )
{
return fs::is_directory(path, error);
}
if( error )
{
return false;
}
return fs::create_directories(path, error);
#else
std::vector<std::wstring> path = stringSplit( m_abstractPathName, pathSeparator );
std::wstring pathToHere = L"";
@ -208,6 +289,7 @@ bool File::mkdirs() const
assert( exists() );
return true;
#endif
}
/*
@ -223,7 +305,10 @@ return (File *) parent;
bool File::exists() const
{
// TODO 4J Stu - Possible we could get an error result from something other than the file not existing?
#if defined(_UNICODE)
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
return fs::exists(ToFilesystemPath(getPath()), error);
#elif defined(_UNICODE)
return GetFileAttributes( getPath().c_str() ) != -1;
#elif defined(__linux__)
// BAD DOBBY BAD DOBBY
@ -252,6 +337,16 @@ bool File::isFile() const
//true if and only if the renaming succeeded; false otherwise
bool File::renameTo(File dest)
{
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
fs::rename(ToFilesystemPath(getPath()), ToFilesystemPath(dest.getPath()), error);
if(error)
{
perror("File::renameTo - Error renaming file");
return false;
}
return true;
#else
std::string sourcePath = wstringtofilename(getPath());
std::string destPath = wstringtofilename(dest.getPath());
@ -266,37 +361,9 @@ bool File::renameTo(File dest)
{
return true;
}
#endif
}
#if defined(__linux__)
// DecalOverdose: Stolen from my other project
void listFiles(const char *directory) {
DIR *dp;
struct dirent *entry;
struct stat fileStat;
dp = opendir(directory);
if (dp == NULL) {
perror("opendir");
return;
}
while ((entry = readdir(dp)) != NULL) {
char fullPath[1024];
snprintf(fullPath, sizeof(fullPath), "%s/%s", directory, entry->d_name);
if (stat(fullPath, &fileStat) == 0) {
if (S_ISREG(fileStat.st_mode)) {
printf("File: %s\n", entry->d_name);
}
} else {
perror("stat");
}
}
closedir(dp);
}
#endif // __linux__
//Returns an array of abstract pathnames denoting the files in the directory denoted by this abstract pathname.
//If this abstract pathname does not denote a directory, then this method returns null. Otherwise an array of File objects is returned,
//one for each file or directory in the directory. Pathnames denoting the directory itself and the directory's parent directory
@ -319,6 +386,13 @@ std::vector<File *> *File::listFiles() const
if( !isDirectory() )
return vOutput;
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
for( fs::directory_iterator it(ToFilesystemPath(getPath()), error); !error && it != fs::directory_iterator(); it.increment(error) )
{
vOutput->push_back( new File( *this, ToFilename(it->path()) ) );
}
#else
#ifdef __PS3__
const char *lpFileName=wstringtofilename(getPath());
char filePath[256];
@ -447,6 +521,7 @@ std::vector<File *> *File::listFiles() const
FindClose( hFind);
}
#endif
#endif
#endif
return vOutput;
}
@ -469,6 +544,17 @@ std::vector<File *> *File::listFiles(FileFilter *filter) const
std::vector<File *> *vOutput = new std::vector<File *>();
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
for( fs::directory_iterator it(ToFilesystemPath(getPath()), error); !error && it != fs::directory_iterator(); it.increment(error) )
{
File thisFile = File( *this, ToFilename(it->path()) );
if( filter->accept( &thisFile ) )
{
vOutput->push_back( new File( thisFile ) );
}
}
#else
#ifdef __PS3__
const char *lpFileName=wstringtofilename(getPath());
char filePath[256];
@ -551,6 +637,7 @@ std::vector<File *> *File::listFiles(FileFilter *filter) const
FindClose( hFind);
}
#endif
#endif
#endif
return vOutput;
}
@ -560,7 +647,10 @@ std::vector<File *> *File::listFiles(FileFilter *filter) const
//true if and only if the file denoted by this abstract pathname exists and is a directory; false otherwise
bool File::isDirectory() const
{
#if defined(_UNICODE)
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
return fs::is_directory(ToFilesystemPath(getPath()), error);
#elif defined(_UNICODE)
return exists() && ( GetFileAttributes( getPath().c_str() ) & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
#elif defined(__linux__)
const char *dirpath = wstringtofilename(getPath());
@ -639,17 +729,20 @@ __int64 File::length()
return 0;
}
return statData.fileSize;
#elif defined(__linux__)
struct stat fileInfoBuffer;
const char *path = wstringtofilename(getPath());
#elif !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
const fs::path path = ToFilesystemPath(getPath());
int result = stat(path, &fileInfoBuffer);
if(result == 0) {
return fileInfoBuffer.st_size;
} else {
return 0;
if( fs::is_regular_file(path, error) )
{
const auto size = fs::file_size(path, error);
if( !error )
{
return static_cast<__int64>(size);
}
}
return 0;
#else
WIN32_FILE_ATTRIBUTE_DATA fileInfoBuffer;
#ifdef _UNICODE
@ -689,7 +782,21 @@ __int64 File::length()
//or 0L if the file does not exist or if an I/O error occurs
__int64 File::lastModified()
{
#if !defined(__linux__)
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
std::error_code error;
const fs::path path = ToFilesystemPath(getPath());
if( fs::is_regular_file(path, error) )
{
const fs::file_time_type lastWriteTime = fs::last_write_time(path, error);
if( !error )
{
return ToEpochMilliseconds(lastWriteTime);
}
}
return 0l;
#elif !defined(__linux__)
WIN32_FILE_ATTRIBUTE_DATA fileInfoBuffer;
#ifdef _UNICODE
BOOL result = GetFileAttributesEx(

View file

@ -1,4 +1,5 @@
#include "../Platform/stdafx.h"
#include "../Util/PortableFileIO.h"
#include "../Headers/net.minecraft.world.level.h"
#include "../Headers/net.minecraft.world.level.biome.h"
#include "../Headers/net.minecraft.world.level.levelgen.h"
@ -19,86 +20,47 @@ CustomLevelSource::CustomLevelSource(Level *level, __int64 seed, bool generateSt
m_heightmapOverride = byteArray( (m_XZSize*16) * (m_XZSize*16) );
#ifdef _UNICODE
std::wstring path = L"GAME:\\GameRules\\heightmap.bin";
#else
#ifdef _WINDOWS64
std::string path = "GameRules\\heightmap.bin";
const std::wstring path = L"GameRules\\heightmap.bin";
#else
std::string path = "GAME:\\GameRules\\heightmap.bin";
const std::wstring path = L"GAME:\\GameRules\\heightmap.bin";
#endif
#endif
HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( file == INVALID_HANDLE_VALUE )
const PortableFileIO::BinaryReadResult heightmapReadResult = PortableFileIO::ReadBinaryFile(path, m_heightmapOverride.data, m_heightmapOverride.length);
if(heightmapReadResult.status == PortableFileIO::BinaryReadStatus::not_found)
{
app.FatalLoadError();
DWORD error = GetLastError();
assert(false);
}
else
else if(heightmapReadResult.status == PortableFileIO::BinaryReadStatus::too_large)
{
#ifdef _DURANGO
__debugbreak(); // TODO
DWORD bytesRead,dwFileSize = 0;
#else
DWORD bytesRead,dwFileSize = GetFileSize(file,NULL);
#endif
if(dwFileSize > m_heightmapOverride.length)
{
app.DebugPrintf("Heightmap binary is too large!!\n");
__debugbreak();
}
BOOL bSuccess = ReadFile(file,m_heightmapOverride.data,dwFileSize,&bytesRead,NULL);
if(bSuccess==FALSE)
{
app.FatalLoadError();
}
CloseHandle(file);
app.DebugPrintf("Heightmap binary is too large!!\n");
__debugbreak();
}
else if(heightmapReadResult.status != PortableFileIO::BinaryReadStatus::ok)
{
app.FatalLoadError();
}
m_waterheightOverride = byteArray( (m_XZSize*16) * (m_XZSize*16) );
#ifdef _UNICODE
std::wstring waterHeightPath = L"GAME:\\GameRules\\waterheight.bin";
#else
#ifdef _WINDOWS64
std::string waterHeightPath = "GameRules\\waterheight.bin";
const std::wstring waterHeightPath = L"GameRules\\waterheight.bin";
#else
std::string waterHeightPath = "GAME:\\GameRules\\waterheight.bin";
const std::wstring waterHeightPath = L"GAME:\\GameRules\\waterheight.bin";
#endif
#endif
file = CreateFile(waterHeightPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( file == INVALID_HANDLE_VALUE )
const PortableFileIO::BinaryReadResult waterHeightReadResult = PortableFileIO::ReadBinaryFile(waterHeightPath, m_waterheightOverride.data, m_waterheightOverride.length);
if(waterHeightReadResult.status == PortableFileIO::BinaryReadStatus::not_found)
{
DWORD error = GetLastError();
//assert(false);
memset(m_waterheightOverride.data, level->seaLevel, m_waterheightOverride.length);
}
else
else if(waterHeightReadResult.status == PortableFileIO::BinaryReadStatus::too_large)
{
#ifdef _DURANGO
__debugbreak(); // TODO
DWORD bytesRead,dwFileSize = 0;
#else
DWORD bytesRead,dwFileSize = GetFileSize(file,NULL);
#endif
if(dwFileSize > m_waterheightOverride.length)
{
app.DebugPrintf("waterheight binary is too large!!\n");
__debugbreak();
}
BOOL bSuccess = ReadFile(file,m_waterheightOverride.data,dwFileSize,&bytesRead,NULL);
if(bSuccess==FALSE)
{
app.FatalLoadError();
}
CloseHandle(file);
app.DebugPrintf("waterheight binary is too large!!\n");
__debugbreak();
}
else if(waterHeightReadResult.status != PortableFileIO::BinaryReadStatus::ok)
{
app.FatalLoadError();
}
caveFeature = new LargeCaveFeature();

View file

@ -3,33 +3,17 @@
PerformanceTimer::PerformanceTimer()
{
#if !defined (__linux__)
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec;
QueryPerformanceFrequency( &qwTicksPerSec );
m_fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
Reset();
#endif
}
void PerformanceTimer::Reset()
{
#if !defined (__linux__)
QueryPerformanceCounter( &m_qwStartTime );
#endif
m_startTime = std::chrono::steady_clock::now();
}
void PerformanceTimer::PrintElapsedTime(const std::wstring &description)
{
#if !defined (__linux__)
LARGE_INTEGER qwNewTime, qwDeltaTime;
const std::chrono::duration<float> elapsedTime = std::chrono::steady_clock::now() - m_startTime;
QueryPerformanceCounter( &qwNewTime );
qwDeltaTime.QuadPart = qwNewTime.QuadPart - m_qwStartTime.QuadPart;
float fElapsedTime = m_fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
app.DebugPrintf("TIMER: %ls: Elapsed time %f\n", description.c_str(), fElapsedTime);
#endif
app.DebugPrintf("TIMER: %ls: Elapsed time %f\n", description.c_str(), elapsedTime.count());
}

View file

@ -1,13 +1,24 @@
#pragma once
#ifdef __valid
#pragma push_macro("__valid")
#undef __valid
#define PERFORMANCE_TIMER_RESTORE_VALID_MACRO
#endif
#include <chrono>
#ifdef PERFORMANCE_TIMER_RESTORE_VALID_MACRO
#pragma pop_macro("__valid")
#undef PERFORMANCE_TIMER_RESTORE_VALID_MACRO
#endif
#include <string>
class PerformanceTimer
{
private:
LARGE_INTEGER m_qwStartTime;
float m_fSecsPerTick;
std::chrono::steady_clock::time_point m_startTime;
public:
PerformanceTimer();
void Reset();
void PrintElapsedTime(const std::wstring &description);
};
};

View file

@ -0,0 +1,90 @@
#pragma once
#include <cstddef>
#include <fstream>
#include <ios>
#include <string>
#include "StringHelpers.h"
namespace PortableFileIO
{
enum class BinaryReadStatus
{
ok,
not_found,
too_large,
read_error,
};
struct BinaryReadResult
{
BinaryReadStatus status;
std::size_t bytesRead;
std::size_t fileSize;
};
inline BinaryReadResult ReadBinaryFile(const std::wstring &path, void *buffer, std::size_t capacity)
{
const std::string nativePath = wstringtofilename(path);
std::ifstream stream(nativePath.c_str(), std::ios::binary | std::ios::ate);
if (!stream.is_open())
{
return { BinaryReadStatus::not_found, 0, 0 };
}
const std::streamoff endPosition = stream.tellg();
if (endPosition < 0)
{
return { BinaryReadStatus::read_error, 0, 0 };
}
const std::size_t fileSize = static_cast<std::size_t>(endPosition);
if (fileSize > capacity)
{
return { BinaryReadStatus::too_large, 0, fileSize };
}
stream.seekg(0, std::ios::beg);
stream.read(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(fileSize));
const std::size_t bytesRead = static_cast<std::size_t>(stream.gcount());
if ((!stream && !stream.eof()) || bytesRead != fileSize)
{
return { BinaryReadStatus::read_error, bytesRead, fileSize };
}
return { BinaryReadStatus::ok, bytesRead, fileSize };
}
inline BinaryReadResult ReadBinaryFileSegment(const std::wstring &path, std::size_t offset, void *buffer, std::size_t bytesToRead)
{
const std::string nativePath = wstringtofilename(path);
std::ifstream stream(nativePath.c_str(), std::ios::binary | std::ios::ate);
if (!stream.is_open())
{
return { BinaryReadStatus::not_found, 0, 0 };
}
const std::streamoff endPosition = stream.tellg();
if (endPosition < 0)
{
return { BinaryReadStatus::read_error, 0, 0 };
}
const std::size_t fileSize = static_cast<std::size_t>(endPosition);
if ((offset > fileSize) || (bytesToRead > (fileSize - offset)))
{
return { BinaryReadStatus::too_large, 0, fileSize };
}
stream.seekg(static_cast<std::streamoff>(offset), std::ios::beg);
stream.read(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bytesToRead));
const std::size_t bytesRead = static_cast<std::size_t>(stream.gcount());
if ((!stream && !stream.eof()) || bytesRead != bytesToRead)
{
return { BinaryReadStatus::read_error, bytesRead, fileSize };
}
return { BinaryReadStatus::ok, bytesRead, fileSize };
}
}

View file

@ -1,4 +1,5 @@
#include "../../Platform/stdafx.h"
#include "../../Util/PortableFileIO.h"
#include "../../Headers/net.minecraft.world.level.biome.h"
#include "../../Headers/net.minecraft.world.level.newbiome.layer.h"
#include "../../Headers/net.minecraft.world.level.h"
@ -9,47 +10,25 @@ BiomeOverrideLayer::BiomeOverrideLayer(int seedMixup) : Layer(seedMixup)
{
m_biomeOverride = byteArray( width * height );
#ifdef _UNICODE
std::wstring path = L"GAME:\\GameRules\\biomemap.bin";
HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#else
#ifdef _WINDOWS64
std::string path = "GameRules\\biomemap.bin";
const std::wstring path = L"GameRules\\biomemap.bin";
#else
std::string path = "GAME:\\GameRules\\biomemap.bin";
const std::wstring path = L"GAME:\\GameRules\\biomemap.bin";
#endif
HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#endif
if( file == INVALID_HANDLE_VALUE )
const PortableFileIO::BinaryReadResult readResult = PortableFileIO::ReadBinaryFile(path, m_biomeOverride.data, m_biomeOverride.length);
if(readResult.status == PortableFileIO::BinaryReadStatus::not_found)
{
DWORD error = GetLastError();
//assert(false);
app.DebugPrintf("Biome override not found, using plains as default\n");
memset(m_biomeOverride.data,Biome::plains->id,m_biomeOverride.length);
}
else
else if(readResult.status == PortableFileIO::BinaryReadStatus::too_large)
{
#ifdef _DURANGO
__debugbreak(); // TODO
DWORD bytesRead,dwFileSize = 0;
#else
DWORD bytesRead,dwFileSize = GetFileSize(file,NULL);
#endif
if(dwFileSize > m_biomeOverride.length)
{
app.DebugPrintf("Biomemap binary is too large!!\n");
__debugbreak();
}
BOOL bSuccess = ReadFile(file,m_biomeOverride.data,dwFileSize,&bytesRead,NULL);
if(bSuccess==FALSE)
{
app.FatalLoadError();
}
CloseHandle(file);
app.DebugPrintf("Biomemap binary is too large!!\n");
__debugbreak();
}
else if(readResult.status != PortableFileIO::BinaryReadStatus::ok)
{
app.FatalLoadError();
}
}
@ -78,4 +57,4 @@ intArray BiomeOverrideLayer::getArea(int xo, int yo, int w, int h)
}
}
return result;
}
}