From aa77a5847f2494751ad2c9878ed44119001bde52 Mon Sep 17 00:00:00 2001 From: AdventureT Date: Sat, 2 Dec 2023 17:17:13 +0100 Subject: [PATCH] Implemented TFile Fixed TCString::Copy Bug --- .gitignore | 3 + .../TApplication/TApplication_Tests.cpp | 5 +- .../UnitTests/Source/TKernel/TFile_Tests.cpp | 12 +- .../Source/TKernel/TKernelInterface_Tests.cpp | 5 +- Tools/UnitTests/Source/main.cpp | 19 +- Toshi/Include/Defines.h | 3 +- Toshi/Include/TKernel/TFile.h | 36 +- Toshi/Include/TKernel/TMemory.h | 19 +- Toshi/Include/TKernel/Win/TNativeFileWin.h | 69 +++ Toshi/Source/TKernel/TCString.cpp | 9 +- Toshi/Source/TKernel/TFile.cpp | 6 +- Toshi/Source/TKernel/TKernelInterface.cpp | 15 + Toshi/Source/TKernel/TMemory.cpp | 18 + Toshi/Source/TKernel/Win/TNativeFileWin.cpp | 422 ++++++++++++++++++ 14 files changed, 596 insertions(+), 45 deletions(-) create mode 100644 Toshi/Include/TKernel/Win/TNativeFileWin.h create mode 100644 Toshi/Source/TKernel/Win/TNativeFileWin.cpp diff --git a/.gitignore b/.gitignore index 1cf1b8a..00a2868 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ *.lib *.lib.* *.exe.* +bin +bin-int +act.exe # User-specific files *.rsuser diff --git a/Tools/UnitTests/Source/TApplication/TApplication_Tests.cpp b/Tools/UnitTests/Source/TApplication/TApplication_Tests.cpp index 9709ea7..9c8716a 100644 --- a/Tools/UnitTests/Source/TApplication/TApplication_Tests.cpp +++ b/Tools/UnitTests/Source/TApplication/TApplication_Tests.cpp @@ -5,8 +5,5 @@ TOSHI_NAMESPACE_USING TEST_CASE("Create", "[TApplication]") { - TApplication app; - TPCHAR argv[2] = {(TPCHAR)"Path", (TPCHAR)"Hello there"}; - TBOOL bRes = app.Create("Test", 1, argv); - REQUIRE(bRes); + } \ No newline at end of file diff --git a/Tools/UnitTests/Source/TKernel/TFile_Tests.cpp b/Tools/UnitTests/Source/TKernel/TFile_Tests.cpp index 0227f6d..3c598dd 100644 --- a/Tools/UnitTests/Source/TKernel/TFile_Tests.cpp +++ b/Tools/UnitTests/Source/TKernel/TFile_Tests.cpp @@ -1,10 +1,16 @@ -#include "TKernel/TFile.h" #include +#include +#include +#include +#include "TKernel/TDebug.h" +#include "TKernel/Win/TNativeFileWin.h" TOSHI_NAMESPACE_USING TEST_CASE("Create File", "[TFile]") { - TFileManager* pFileManager = new TFileManager(); - TFile::Create("Test.txt", TMODE_CREATE); + TFile* f = TFile::Create("Test.txt", TMODE_CREATE); + f->Destroy(); + std::ifstream f2("Test.txt"); + REQUIRE(f2.good()); } \ No newline at end of file diff --git a/Tools/UnitTests/Source/TKernel/TKernelInterface_Tests.cpp b/Tools/UnitTests/Source/TKernel/TKernelInterface_Tests.cpp index ad36310..e941ead 100644 --- a/Tools/UnitTests/Source/TKernel/TKernelInterface_Tests.cpp +++ b/Tools/UnitTests/Source/TKernel/TKernelInterface_Tests.cpp @@ -5,6 +5,7 @@ TOSHI_NAMESPACE_USING TEST_CASE("DumpInfo", "[TKernelInterface]") { - TKernelInterface ki = TKernelInterface(0, (TPCHAR*)"", TTRUE); - ki.DumpInfo(); + + //TKernelInterface ki = TKernelInterface(0, (TPCHAR*)"", TTRUE); + //ki.DumpInfo(); } \ No newline at end of file diff --git a/Tools/UnitTests/Source/main.cpp b/Tools/UnitTests/Source/main.cpp index 126c859..f329705 100644 --- a/Tools/UnitTests/Source/main.cpp +++ b/Tools/UnitTests/Source/main.cpp @@ -1,8 +1,23 @@ #include +#include "TApplication/TApplication.h" -int main(int argc, char* argv[]) { +TOSHI_NAMESPACE_USING - int result = Catch::Session().run(argc, argv); +class AApplication : public Toshi::TApplication +{ +}; + +static AApplication g_oTheApp; + +int main(int argc, char* argv[]) +{ + int result; + if (g_oTheApp.Create("UnitTests", argc, argv)) { + //g_oTheApp.Execute(); + result = Catch::Session().run(argc, argv); + } + + g_oTheApp.Destroy(); return result; } \ No newline at end of file diff --git a/Toshi/Include/Defines.h b/Toshi/Include/Defines.h index 7f81c48..4515a8a 100644 --- a/Toshi/Include/Defines.h +++ b/Toshi/Include/Defines.h @@ -39,7 +39,8 @@ typedef unsigned int TUINT32; typedef unsigned __int64 TUINT64; typedef short TSHORT; typedef unsigned short TUSHORT; -typedef unsigned short TWCHAR; +typedef wchar_t TWCHAR; +typedef unsigned short* TPWCHAR; typedef const char* TPCCHAR; typedef char* TPCHAR; typedef char TCHAR; diff --git a/Toshi/Include/TKernel/TFile.h b/Toshi/Include/TKernel/TFile.h index 5307726..8474311 100644 --- a/Toshi/Include/TKernel/TFile.h +++ b/Toshi/Include/TKernel/TFile.h @@ -59,8 +59,17 @@ class TOSHI_EXPORT TFileManager class TSysPathIter { public: - TSysPathIter(const TCString &a_rsSysPath) : m_sSysPath(a_rsSysPath), m_iPosition(-1) { }; - TSysPathIter(const TSysPathIter &a_rOther) : m_sSysPath(a_rOther.m_sSysPath), m_iPosition(a_rOther.m_iPosition) { }; + TSysPathIter(const TCString &a_rsSysPath) + { + m_sSysPath = a_rsSysPath; + m_iPosition = -1; + } + + TSysPathIter(const TSysPathIter &a_rOther) + { + m_sSysPath = a_rOther.m_sSysPath; + m_iPosition = a_rOther.m_iPosition; + } TBOOL First(TCString &a_rSysPath) { @@ -90,19 +99,24 @@ class TOSHI_EXPORT TFileManager } private: - const TCString& m_sSysPath; // 0x0 - TINT m_iPosition; // 0x4 + TCString m_sSysPath; // 0x0 + TINT m_iPosition; // 0x4 }; TFileManager(); ~TFileManager(); - TFile* CreateFile(const TCString& a_sName, TUINT a_uiMode); + TFile* CreateFile(TCString const &a_sName, TUINT a_uiMode); TFileSystem* FindFileSystem(const TCString& a_rFileSysName); void MountFileSystem(TFileSystem* a_pFileSystem); void UnmountFileSystem(TFileSystem* a_pFileSystem); static TFileManager* __stdcall GetFileManager() { return s_pFileManager; } + void SetSystemPath(TCString const& a_rSysPath) + { + m_pcSystemPath = a_rSysPath; + InvalidateSystemPath(); + } private: void ValidateSystemPath(); @@ -125,6 +139,7 @@ enum TMODE : TUINT TMODE_WRITEONLY = BITFIELD(1), TMODE_READWRITE = BITFIELD(2), TMODE_CREATE = BITFIELD(3), + TMODE_NOBUFFER = BITFIELD(4) }; class TOSHI_EXPORT TFile @@ -134,13 +149,18 @@ class TOSHI_EXPORT TFile { m_pFileSystem = a_pFileSystem; } + virtual ~TFile() = default; public: enum TSEEK { - + TSEEK_SET, + TSEEK_CUR, + TSEEK_END }; - + + virtual TINT Read(TPVOID a_pBuffer, TINT m_iSize) = 0; + virtual TINT Write(void const *a_pBuffer, TINT m_iSize) = 0; virtual TBOOL Seek(TINT a_iOffset, TSEEK a_eSeek) = 0; virtual TINT Tell() = 0; virtual TINT GetSize() = 0; @@ -159,7 +179,7 @@ class TOSHI_EXPORT TFile void Destroy(); TFileSystem* GetFileSystem() const { return m_pFileSystem; } -private: +protected: // 0x0 vftable TFileSystem* m_pFileSystem; // 0x4 }; diff --git a/Toshi/Include/TKernel/TMemory.h b/Toshi/Include/TKernel/TMemory.h index 2639030..ef91bf1 100644 --- a/Toshi/Include/TKernel/TMemory.h +++ b/Toshi/Include/TKernel/TMemory.h @@ -64,20 +64,5 @@ inline static TMemory g_oMemManager; TOSHI_NAMESPACE_END -TPVOID TOSHI_EXPORT __stdcall tmalloc(TINT a_iSize, TPCHAR a_pBuffer, TINT a_iUnk) -{ -#ifdef TOSHI_NOTFINAL - return malloc(a_iSize); -#else - return Toshi::TMemory::GetMemMangager().Alloc(a_iSize, 16, Toshi::TMemory::GetGlobalBlock(), a_pBuffer, a_iUnk); -#endif -} - -void TOSHI_EXPORT __stdcall tfree(TPVOID a_pMem) -{ -#ifdef TOSHI_NOTFINAL - free(a_pMem); -#else - Toshi::TMemory::GetMemMangager().Free(a_pMem); -#endif -} \ No newline at end of file +TPVOID TOSHI_EXPORT __stdcall tmalloc(TINT a_iSize, TPCHAR a_pBuffer, TINT a_iUnk); +void TOSHI_EXPORT __stdcall tfree(TPVOID a_pMem); \ No newline at end of file diff --git a/Toshi/Include/TKernel/Win/TNativeFileWin.h b/Toshi/Include/TKernel/Win/TNativeFileWin.h new file mode 100644 index 0000000..6b0ff03 --- /dev/null +++ b/Toshi/Include/TKernel/Win/TNativeFileWin.h @@ -0,0 +1,69 @@ +#pragma once +#include "../TFile.h" + +#define BUFFER_SIZE 0x800 + +TOSHI_NAMESPACE_BEGIN + +class TOSHI_EXPORT TNativeFileSystem : public TFileSystem +{ +public: + TNativeFileSystem(TPCCHAR a_pcName); + + virtual TFile* CreateFile(Toshi::TCString const& a_rFilename, TUINT a_uiMode) override; + virtual void DestroyFile(TFile* a_pFile) override; + virtual TBOOL RemoveFile(TCString const& a_rFilename) override; + virtual TCString MakeInternalPath(const TCString& a_rsPath) override; + +private: + HANDLE m_hFileSystem; // 0x1C +}; + +class TOSHI_EXPORT TNativeFileManager : public TFileManager +{ + +}; + +class TOSHI_EXPORT TNativeFile : public TFile +{ + friend class TNativeFileSystem; +public: + + virtual TINT Read(TPVOID a_pBuffer, TINT a_iSize) override; + virtual TINT Write(void const* a_pBuffer, TINT a_iSize) override; + virtual TBOOL Seek(TINT a_iOffset, TSEEK a_eSeek) override; + virtual TINT Tell() override { return m_iPosition; } + virtual TINT GetSize() override; + virtual TUINT64 GetDate() override; + virtual TINT GetCChar() override; + virtual TINT GetWChar() override; + virtual TINT PutCChar(TCHAR a_cChar) override; + virtual TINT PutWChar(TWCHAR a_wcChar) override; + virtual TINT VCPrintf(TCHAR const* a_pFormat, va_list a_args) override; + virtual TINT VWPrintf(TWCHAR const* a_pFormat, va_list a_args) override; + +protected: + TNativeFile(TNativeFileSystem *a_pFileManager); + virtual ~TNativeFile() = default; + + TBOOL Open(const TCString &a_rFileName, TUINT a_uiMode); + void Close(); + +private: + TINT ReadUnbuffered(TPVOID a_pBuffer, TINT a_iSize); + TBOOL LoadBuffer(TINT a_iBufferPos); + TINT FlushWriteBuffer(); + +private: + HANDLE m_hFile; // 0x8 + TINT m_iPosition; // 0xC + TINT m_iBufferPosition; // 0x10 + TINT m_iPrevBufferPos; // 0x14 + TINT m_iLastBufferSize; // 0x18 + TPVOID m_pBuffer; // 0x1C + TPVOID m_pWriteBuffer; // 0x20 + TINT m_iWriteBufferUsed; // 0x24 + TBOOL m_bWriteBuffered; // 0x28 +}; + +TOSHI_NAMESPACE_END \ No newline at end of file diff --git a/Toshi/Source/TKernel/TCString.cpp b/Toshi/Source/TKernel/TCString.cpp index 6fbf937..828b103 100644 --- a/Toshi/Source/TKernel/TCString.cpp +++ b/Toshi/Source/TKernel/TCString.cpp @@ -53,18 +53,17 @@ TINT TCString::Compare(TPCCHAR a_pcString, int a_iLength) const void TCString::Copy(const TCString& a_rOther, TINT a_iLength) { - if (*this != a_rOther) - { - if (m_iStrLen < a_iLength || a_iLength == -1) a_iLength = m_iStrLen; + if (*this != a_rOther) { + if (a_rOther.m_iStrLen < a_iLength || a_iLength == -1) a_iLength = a_rOther.m_iStrLen; AllocBuffer(a_iLength); + TSystem::MemCopy(m_pBuffer, a_rOther.m_pBuffer, a_iLength); m_pBuffer[a_iLength] = '\0'; } } void TCString::Copy(TPCCHAR a_pcString, TINT a_iLength) { - if (m_pBuffer != a_pcString) - { + if (m_pBuffer != a_pcString) { TINT iLength = a_pcString ? TSystem::StringLength(a_pcString) : 0; if (iLength < a_iLength || a_iLength == -1) a_iLength = iLength; AllocBuffer(a_iLength, TTRUE); diff --git a/Toshi/Source/TKernel/TFile.cpp b/Toshi/Source/TKernel/TFile.cpp index 7d19ee0..cf14276 100644 --- a/Toshi/Source/TKernel/TFile.cpp +++ b/Toshi/Source/TKernel/TFile.cpp @@ -37,7 +37,7 @@ TFileManager::~TFileManager() s_pFileManager = TNULL; } -TFile* TFileManager::CreateFile(const TCString& a_sName, TUINT a_uiMode) +TFile* TFileManager::CreateFile(TCString const& a_sName, TUINT a_uiMode) { TASSERT(a_sName.Length() > 0); ValidateSystemPath(); @@ -77,14 +77,14 @@ TFileSystem* TFileManager::FindFileSystem(const TCString& a_rFileSysName) void TFileManager::MountFileSystem(TFileSystem* a_pFileSystem) { - TASSERT(FindFileSystem(a_pFileSystem->GetName()) == NULL); + TASSERT(FindFileSystem(a_pFileSystem->GetName()) == TNULL); m_aInvalidated.InsertTail(a_pFileSystem); InvalidateSystemPath(); } void TFileManager::UnmountFileSystem(TFileSystem* a_pFileSystem) { - TASSERT(FindFileSystem(a_pFileSystem->GetName()) == NULL); + TASSERT(FindFileSystem(a_pFileSystem->GetName()) == TNULL); a_pFileSystem->Remove(); InvalidateSystemPath(); } diff --git a/Toshi/Source/TKernel/TKernelInterface.cpp b/Toshi/Source/TKernel/TKernelInterface.cpp index bf73160..d1bee08 100644 --- a/Toshi/Source/TKernel/TKernelInterface.cpp +++ b/Toshi/Source/TKernel/TKernelInterface.cpp @@ -1,5 +1,8 @@ #include "TKernelInterface.h" #include +#include // _getcwd +#include +#include TOSHI_MULTIPLATFORM(TNativeFile) TOSHI_NAMESPACE_USING @@ -8,6 +11,18 @@ IMPLEMENT_DYNAMIC(TKernelInterface, TObject); TKernelInterface::TKernelInterface(TINT argc, TPCHAR* const argv, TBOOL a_bVerbose) { TWARNING("TKernelInterface::TKernelInterface() not implemented"); + TCHAR pPath[260]; + TPCHAR pBuffer = _getcwd(pPath, sizeof(pPath)); + TVALIDADDRESS(pBuffer); + TFileManager *pFileManager = new TFileManager(); + TVALIDADDRESS(pFileManager); + TNativeFileSystem *pLocalSystem = new TNativeFileSystem("local"); + TVALIDADDRESS(pLocalSystem); + pLocalSystem->SetPrefix(pBuffer); + TNativeFileSystem *pAbsSystem = new TNativeFileSystem("abs"); + TVALIDADDRESS(pAbsSystem); + pAbsSystem->SetPrefix(""); + pFileManager->SetSystemPath("local"); } TBOOL TKernelInterface::Update() diff --git a/Toshi/Source/TKernel/TMemory.cpp b/Toshi/Source/TKernel/TMemory.cpp index db84b03..6237e46 100644 --- a/Toshi/Source/TKernel/TMemory.cpp +++ b/Toshi/Source/TKernel/TMemory.cpp @@ -57,3 +57,21 @@ TBOOL TMemory::Free(TPVOID a_pMem) } return TBOOL(); } + +TPVOID TOSHI_EXPORT __stdcall tmalloc(TINT a_iSize, TPCHAR a_pBuffer, TINT a_iUnk) +{ +#ifdef TOSHI_NOTFINAL + return malloc(a_iSize); +#else + return Toshi::TMemory::GetMemMangager().Alloc(a_iSize, 16, Toshi::TMemory::GetGlobalBlock(), a_pBuffer, a_iUnk); +#endif +} + +void TOSHI_EXPORT __stdcall tfree(TPVOID a_pMem) +{ +#ifdef TOSHI_NOTFINAL + free(a_pMem); +#else + Toshi::TMemory::GetMemMangager().Free(a_pMem); +#endif +} diff --git a/Toshi/Source/TKernel/Win/TNativeFileWin.cpp b/Toshi/Source/TKernel/Win/TNativeFileWin.cpp new file mode 100644 index 0000000..0b769bf --- /dev/null +++ b/Toshi/Source/TKernel/Win/TNativeFileWin.cpp @@ -0,0 +1,422 @@ +#include "Win/TNativeFileWin.h" +#include "TMemory.h" + +TOSHI_NAMESPACE_USING + +TNativeFileSystem::TNativeFileSystem(TPCCHAR a_pcName) : TFileSystem(a_pcName) +{ + m_hFileSystem = INVALID_HANDLE_VALUE; + TFileManager* pFileManager = TFileManager::GetFileManager(); + TVALIDADDRESS(pFileManager); + pFileManager->MountFileSystem(this); +} + +TFile* TNativeFileSystem::CreateFile(Toshi::TCString const& a_rFilename, TUINT a_uiMode) +{ + TNativeFile* pFile = new TNativeFile(this); + + if (!pFile->Open(a_rFilename, a_uiMode)) { + delete pFile; + return TNULL; + } + + return pFile; +} + +void TNativeFileSystem::DestroyFile(TFile* a_pFile) +{ + TNativeFile* pFile = static_cast(a_pFile); + if (pFile) { + pFile->Close(); + delete pFile; + } +} + +TBOOL TNativeFileSystem::RemoveFile(TCString const& a_rFilename) +{ + return DeleteFile(a_rFilename.GetString()); +} + +TCString TNativeFileSystem::MakeInternalPath(const TCString& a_rsPath) +{ + return TCString(); +} + +TINT TNativeFile::Read(TPVOID a_pBuffer, TINT a_iSize) +{ + if (a_iSize < 1) { + return 0; + } + if (m_pBuffer == TNULL) { + return ReadUnbuffered(a_pBuffer, a_iSize); + } + + TINT readedCount = 0; + TINT startPos = m_iPosition; + TINT curBufferPos = startPos / BUFFER_SIZE * BUFFER_SIZE; + TINT newBufferPos = (startPos + a_iSize) / BUFFER_SIZE * BUFFER_SIZE; + TPVOID curPosBuffer = a_pBuffer; + + if (curBufferPos != newBufferPos) { + if (curBufferPos == m_iPrevBufferPos) + { + TINT readCount = m_iLastBufferSize - (startPos - curBufferPos); + + if (readCount > 0) + { + memcpy(a_pBuffer, (TPCHAR)m_pBuffer + startPos - curBufferPos, readCount); + + curPosBuffer = (TPCHAR)m_pBuffer + readCount; + m_iPosition += readCount; + readedCount = readCount; + } + } + TINT toReadCount = newBufferPos - m_iPosition; + curBufferPos = newBufferPos; + + if (toReadCount > 0) + { + TINT readed = ReadUnbuffered(curPosBuffer, toReadCount); + curPosBuffer = (TPCHAR)curPosBuffer + readed; + readedCount += readed; + + if (readed != toReadCount) + { + // end of file? + return readedCount; + } + } + } + + if (readedCount != a_iSize && LoadBuffer(curBufferPos)) + { + a_iSize -= readedCount; + DWORD bufferLeftSize = m_iPosition - curBufferPos; + DWORD readCount = m_iLastBufferSize - bufferLeftSize; + if (readCount < a_iSize) { + readCount = a_iSize; + } + + if (readCount > 0) + { + memcpy(curPosBuffer, (TPCHAR)m_pBuffer + bufferLeftSize, readCount); + m_iPosition += readCount; + readedCount += readCount; + } + } + + return readedCount; +} + +TINT TNativeFile::Write(void const* a_pBuffer, TINT a_iSize) +{ + if (m_iBufferPosition != m_iPosition) { + m_iBufferPosition = SetFilePointer(m_hFile, m_iPosition, NULL, FILE_BEGIN); + + if (m_iBufferPosition == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + return 0; + } + + m_iPosition = m_iBufferPosition; + } + + if (!m_bWriteBuffered) { + DWORD written; + BOOL bRes = WriteFile(m_hFile, a_pBuffer, a_iSize, &written, NULL); + + if (!bRes) { + return 0; + } + + m_iBufferPosition += written; + m_iPosition = m_iBufferPosition; + return written; + } + + // Flush write buffer if data doesn't fit it + if (m_iWriteBufferUsed + a_iSize >= BUFFER_SIZE) { + FlushWriteBuffer(); + } + + if (a_iSize < BUFFER_SIZE) { + // Data fits the write buffer so append it to it + memcpy((TPCHAR)m_pWriteBuffer + m_iWriteBufferUsed, a_pBuffer, a_iSize); + m_iWriteBufferUsed += a_iSize; + return a_iSize; + } + else { + // Data doesn't fit the write buffer at all so write it right now + DWORD written; + BOOL bRes = WriteFile(m_hFile, a_pBuffer, a_iSize, &written, NULL); + + if (bRes) { + m_iBufferPosition += a_iSize; + m_iPosition = m_iBufferPosition; + return written; + } + } + + return 0; +} + +TBOOL TNativeFile::Seek(TINT a_iOffset, TSEEK a_eSeek) +{ + if (a_eSeek == TFile::TSEEK_SET) { + m_iPosition = a_iOffset < 0 ? 0 : a_iOffset; + } + else if (a_eSeek == TFile::TSEEK_CUR) { + m_iPosition += a_iOffset < 0 ? 0 : a_iOffset; + } + else if (a_eSeek == TFile::TSEEK_END) { + m_iBufferPosition = SetFilePointer(m_hFile, a_iOffset, TNULL, FILE_END); + if (m_iBufferPosition == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + return TFALSE; + } + m_iPosition = m_iBufferPosition; + } + return TTRUE; +} + +TINT TNativeFile::GetSize() +{ + m_iBufferPosition = SetFilePointer(m_hFile, 0, TNULL, TSEEK_END); + if (m_iBufferPosition == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + return 0; + } + return m_iBufferPosition; +} + +TUINT64 TNativeFile::GetDate() +{ + _FILETIME fLastWriteTime; + ULARGE_INTEGER lv_Large; + + lv_Large.LowPart = 0; + lv_Large.HighPart = 0; + + if (m_hFile != TNULL) { + GetFileTime(m_hFile, NULL, NULL, &fLastWriteTime); + lv_Large.LowPart = fLastWriteTime.dwLowDateTime; + lv_Large.HighPart = fLastWriteTime.dwHighDateTime; + } + + return lv_Large.QuadPart; +} + +TINT TNativeFile::GetCChar() +{ + TCHAR result; + + if (m_pBuffer != TNULL) { + TINT curBufferPos = m_iPosition / BUFFER_SIZE * BUFFER_SIZE; + if ((curBufferPos == m_iPrevBufferPos) && (m_iPosition - curBufferPos <= m_iLastBufferSize - 1)) { + result = ((TPCHAR)m_pBuffer)[m_iPosition - curBufferPos]; + m_iPosition += sizeof(result); + return result; + } + } + + return Read(&result, sizeof(result)) == sizeof(result) ? sizeof(result) : -1; +} + +TINT TNativeFile::GetWChar() +{ + TWCHAR result; + + if (m_pBuffer != TNULL) { + TINT curBufferPos = m_iPosition / BUFFER_SIZE * BUFFER_SIZE; + if ((curBufferPos == m_iPrevBufferPos) && (m_iPosition - curBufferPos <= m_iLastBufferSize - 2)) { + result = ((TPWCHAR)m_pBuffer)[m_iPosition - curBufferPos]; + m_iPosition += sizeof(result); + return result; + } + } + + return Read(&result, sizeof(result)) == sizeof(result) ? sizeof(result) : -1; +} + +TINT TNativeFile::PutCChar(TCHAR a_cChar) +{ + return Write(&a_cChar, sizeof(a_cChar)) == sizeof(a_cChar) ? sizeof(a_cChar) : -1; +} + +TINT TNativeFile::PutWChar(TWCHAR a_wcChar) +{ + return Write(&a_wcChar, sizeof(a_wcChar)) == sizeof(a_wcChar) ? sizeof(a_wcChar) : -1; +} + +TINT TNativeFile::VCPrintf(TCHAR const* a_pFormat, va_list a_args) +{ + va_list args; + + va_start(args, a_pFormat); + TCHAR str[0x200]; + TINT iResult = _vsnprintf(str, sizeof(str), a_pFormat, args); + va_end(args); + + return Write(str, iResult); +} + +TINT TNativeFile::VWPrintf(TWCHAR const* a_pFormat, va_list a_args) +{ + va_list args; + + va_start(args, a_pFormat); + TWCHAR str[0x200]; + TINT iResult = _vsnwprintf(str, sizeof(str), a_pFormat, args); + va_end(args); + + return Write(str, iResult * 2) > -1 ? iResult * 2 : iResult; +} + +TNativeFile::TNativeFile(TNativeFileSystem* a_pFileManager) : TFile(a_pFileManager) +{ + m_hFile = INVALID_HANDLE_VALUE; + m_iPosition = -1; + m_iBufferPosition = -1; + m_iPrevBufferPos = -1; + m_iLastBufferSize = 0; + m_pBuffer = TNULL; + m_pWriteBuffer = TNULL; + m_iWriteBufferUsed = 0; + m_bWriteBuffered = TTRUE; +} + +TBOOL TNativeFile::Open(const TCString& a_rFileName, TUINT a_uiMode) +{ + m_pFileSystem->MakeInternalPath(a_rFileName); + + DWORD dwCreationDisposition = 0; + DWORD dwDesiredAccess = 0; + + dwDesiredAccess |= HASFLAG(a_uiMode & TMODE_READONLY) ? GENERIC_READ : dwDesiredAccess; + dwDesiredAccess |= HASFLAG(a_uiMode & TMODE_WRITEONLY) ? GENERIC_WRITE : dwDesiredAccess; + dwDesiredAccess |= HASFLAG(a_uiMode & TMODE_READWRITE) ? (GENERIC_READ | GENERIC_WRITE) : dwDesiredAccess; + + if (HASFLAG(a_uiMode & TMODE_CREATE)) { + dwCreationDisposition = CREATE_ALWAYS; + if (dwDesiredAccess == 0) { + TDPRINTF("WARNING: File created with no access mode, assuming WRITEONLY\n"); + dwDesiredAccess = GENERIC_WRITE; + } + } + else { + dwCreationDisposition = OPEN_EXISTING; + } + + m_hFile = CreateFileA(a_rFileName.GetString(), dwDesiredAccess, 0, NULL, dwCreationDisposition, NULL, NULL); + + if (m_hFile == INVALID_HANDLE_VALUE) { + return TFALSE; + } + + m_iPosition = 0; + m_iBufferPosition = 0; + m_iPrevBufferPos = -1; + m_iLastBufferSize = 0; + + if (HASFLAG(a_uiMode & TMODE_NOBUFFER)) { + m_bWriteBuffered = TFALSE; + } + else { + m_pBuffer = tmalloc(BUFFER_SIZE, TNULL, -1); + TVALIDADDRESS(m_pBuffer); + m_bWriteBuffered = TFALSE; + } + + return TTRUE; +} + +void TNativeFile::Close() +{ + FlushWriteBuffer(); + CloseHandle(m_hFile); + m_hFile = INVALID_HANDLE_VALUE; + m_iPosition = -1; + m_iBufferPosition = -1; + m_iPrevBufferPos = -1; + m_iLastBufferSize = -1; + + if (m_pBuffer != TNULL) { + delete m_pBuffer; + m_pBuffer = TNULL; + } + + if (m_pWriteBuffer != TNULL) { + delete m_pWriteBuffer; + m_pWriteBuffer = TNULL; + } +} + +TINT TNativeFile::ReadUnbuffered(TPVOID a_pBuffer, TINT a_iSize) +{ + DWORD lpNumberOfBytesRead; + + if (m_iPosition != m_iBufferPosition) { + m_iBufferPosition = SetFilePointer(m_hFile, m_iPosition, TNULL, FILE_BEGIN); + + if (m_iBufferPosition == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + return 0; + } + + m_iPosition = m_iBufferPosition; + } + + if (!ReadFile(m_hFile, a_pBuffer, a_iSize, &lpNumberOfBytesRead, TNULL)) { + return 0; + } + + m_iBufferPosition += lpNumberOfBytesRead; + m_iPosition = m_iBufferPosition; + + return lpNumberOfBytesRead; +} + +TBOOL TNativeFile::LoadBuffer(TINT a_iBufferPos) +{ + DWORD lpNumberOfBytesRead; + + if (m_iPrevBufferPos != a_iBufferPos) { + + if (m_iBufferPosition != a_iBufferPos) { + m_iBufferPosition = SetFilePointer(m_hFile, a_iBufferPos, TNULL, FILE_BEGIN); + if (m_iBufferPosition != a_iBufferPos) { + return TFALSE; + } + } + + if (!ReadFile(m_hFile, m_pBuffer, BUFFER_SIZE, &lpNumberOfBytesRead, TNULL)) { + return TFALSE; + } + + m_iBufferPosition += lpNumberOfBytesRead; + m_iLastBufferSize = lpNumberOfBytesRead; + m_iPrevBufferPos = a_iBufferPos; + } + + return TTRUE; +} + +TINT TNativeFile::FlushWriteBuffer() +{ + DWORD lpNumberOfBytesWritten; + + if (m_iPosition != m_iBufferPosition) { + m_iBufferPosition = SetFilePointer(m_hFile, m_iPosition, NULL, FILE_BEGIN); + + if (m_iBufferPosition == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + return 0; + } + + m_iPosition = m_iBufferPosition; + } + + if (!WriteFile(m_hFile, m_pBuffer, m_iWriteBufferUsed, &lpNumberOfBytesWritten, TNULL)) { + return 0; + } + + m_iBufferPosition += lpNumberOfBytesWritten; + m_iPosition = m_iBufferPosition; + m_iWriteBufferUsed = 0; + return lpNumberOfBytesWritten; +}