/* =========================================================================== ARX FATALIS GPL Source Code Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company. This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code'). Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see . In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "HERMES_ClusterSave.h" #include "windows.h" #include "HERMES_hachage.h" #define _CRTDBG_MAP_ALLOC #include //------------------------------------------------------------------------ CCluster::CCluster(int _iTaille) { iTaille = _iTaille; iNext = -1; pNext = NULL; } //------------------------------------------------------------------------ CCluster::~CCluster() { } //------------------------------------------------------------------------ void CInfoFile::Set(char * _pcFileName, int _iTaille) { if (pcFileName) { free((void *)pcFileName); pcFileName = NULL; } pcFileName = strdup(_pcFileName); iTaille = _iTaille; iNbCluster = 0; FirstCluster.iTaille = _iTaille; FirstCluster.iNext = -1; FirstCluster.pNext = NULL; } //------------------------------------------------------------------------ void CInfoFile::KillAll() { if (pcFileName) { free((void *)pcFileName); pcFileName = NULL; } CCluster * _pClusterNext = FirstCluster.pNext; while (_pClusterNext) { CCluster * _pClusterNextNext = _pClusterNext->pNext; delete _pClusterNext; _pClusterNext = _pClusterNextNext; } FirstCluster.iTaille = 0; FirstCluster.iNext = 0; FirstCluster.pNext = NULL; } //------------------------------------------------------------------------ CSaveBlock::CSaveBlock(char * _pcBlockName) { if (_pcBlockName) { pcBlockName = strdup(_pcBlockName); } else { pcBlockName = strdup("Save Block"); } hFile = NULL; iTailleBlock = 0; iNbFiles = 0; sInfoFile = NULL; bFirst = false; pHachage = NULL; } //------------------------------------------------------------------------ CSaveBlock::~CSaveBlock() { if (pcBlockName) { free((void *)pcBlockName); } while (iNbFiles--) { sInfoFile[iNbFiles].KillAll(); } free(sInfoFile); sInfoFile = NULL; if (hFile) { fclose(hFile); hFile = NULL; } delete pHachage; pHachage = NULL; } //------------------------------------------------------------------------ void CSaveBlock::ResetFAT(void) { iTailleBlock = 0; while (iNbFiles--) { sInfoFile[iNbFiles].KillAll(); } free(sInfoFile); sInfoFile = NULL; iNbFiles = 0; } //------------------------------------------------------------------------ bool CSaveBlock::BeginRead(void) { hFile = fopen((const char *)pcBlockName, (const char *)"rb"); if (!hFile) { return false; } //READ FAT int _iI; fread((void *)&_iI, 1, 4, hFile); fseek(hFile, _iI + 4, SEEK_SET); fread((void *)&_iI, 1, 4, hFile); //version fread((void *)&_iI, 1, 4, hFile); int iNbHache = 1; while (iNbHache < _iI) iNbHache <<= 1; int iNbHacheTroisQuart = (iNbHache * 3) / 4; if (_iI > iNbHacheTroisQuart) iNbHache <<= 1; pHachage = new CHachageString(iNbHache); while (_iI--) { char _pT[256], *_pTT = _pT; while (1) { fread((void *)_pTT, 1, 1, hFile); if (!*_pTT) break; _pTT++; } ExpandNbFiles(); CInfoFile * _pInfoFile = &sInfoFile[iNbFiles-1]; fread((void *)&_pInfoFile->iTaille, 1, 4, hFile); _pInfoFile->Set(_pT, _pInfoFile->iTaille); fread((void *)&_pInfoFile->iNbCluster, 1, 4, hFile); fseek(hFile, 4, SEEK_CUR); CCluster * _pCluster = &_pInfoFile->FirstCluster; fread((void *)&_pCluster->iTaille, 1, 4, hFile); fread((void *)&_pCluster->iNext, 1, 4, hFile); _pCluster->pNext = NULL; int _iJ = _pInfoFile->iNbCluster - 1; if (_iJ > 0) { while (_iJ--) { _pCluster->pNext = new CCluster(0); _pCluster = _pCluster->pNext; fread((void *)&_pCluster->iTaille, 1, 4, hFile); fread((void *)&_pCluster->iNext, 1, 4, hFile); } } } for (int i = 0; i < iNbFiles; ++i) { pHachage->AddString(sInfoFile[i].pcFileName, &sInfoFile[i]); } return true; } //------------------------------------------------------------------------ void CSaveBlock::EndRead(void) { if (hFile) { fclose(hFile); hFile = NULL; } ResetFAT(); delete pHachage; pHachage = NULL; } //------------------------------------------------------------------------ bool CSaveBlock::BeginSave(bool _bCont, bool _bReWrite) { bReWrite = _bReWrite; hFile = fopen((const char *)pcBlockName, (const char *)"rb"); if ((!hFile) || (!_bCont)) { hFile = fopen((const char *)pcBlockName, (const char *)"wb"); if (!hFile) return false; int _iI = 0; fwrite((const void *)&_iI, 1, 4, hFile); bFirst = true; } else { fread((void *)&iTailleBlock, 1, 4, hFile); fseek(hFile, iTailleBlock + 4, SEEK_SET); //READ FAT int _iI; fread((void *)&_iI, 1, 4, hFile); //version iVersion = _iI; fread((void *)&_iI, 1, 4, hFile); while (_iI--) { char _pT[256], *_pTT = _pT; while (1) { fread((void *)_pTT, 1, 1, hFile); if (!*_pTT) break; _pTT++; } ExpandNbFiles(); CInfoFile * _pInfoFile = &sInfoFile[iNbFiles-1]; fread((void *)&_pInfoFile->iTaille, 1, 4, hFile); _pInfoFile->Set(_pT, _pInfoFile->iTaille); fread((void *)&_pInfoFile->iNbCluster, 1, 4, hFile); fseek(hFile, 4, SEEK_CUR); CCluster * _pCluster = &_pInfoFile->FirstCluster; fread((void *)&_pCluster->iTaille, 1, 4, hFile); fread((void *)&_pCluster->iNext, 1, 4, hFile); _pCluster->pNext = NULL; int _iJ = _pInfoFile->iNbCluster - 1; if (_iJ > 0) { while (_iJ--) { _pCluster->pNext = new CCluster(0); _pCluster = _pCluster->pNext; fread((void *)&_pCluster->iTaille, 1, 4, hFile); fread((void *)&_pCluster->iNext, 1, 4, hFile); } } } fseek(hFile, 4, SEEK_SET); void * _pPtr = malloc(iTailleBlock); fread((void *)_pPtr, iTailleBlock, 1, hFile); fclose(hFile); hFile = NULL; hFile = fopen((const char *)pcBlockName, (const char *)"w+b"); if (!hFile) return false; _iI = 0; fwrite((const void *)&_iI, 4, 1, hFile); fwrite((const void *)_pPtr, iTailleBlock, 1, hFile); free((void *)_pPtr); } return true; } //------------------------------------------------------------------------ bool CSaveBlock::EndSave(void) { if (!bFirst) { return Defrag(); } int _version = PAK_VERSION + 1; fwrite((const void *)&_version, 1, 4, hFile); fwrite((const void *)&iNbFiles, 1, 4, hFile); for (int _iI = 0; _iI < iNbFiles; _iI++) { fwrite((const void *)sInfoFile[_iI].pcFileName, 1, strlen((const char *)sInfoFile[_iI].pcFileName) + 1, hFile); fwrite((const void *)&sInfoFile[_iI].iTaille, 1, 4, hFile); int _iT = sInfoFile[_iI].iNbCluster; fwrite((const void *)&_iT, 1, 4, hFile); _iT *= (sizeof(CCluster) - 4) + strlen((const char *)sInfoFile[_iI].pcFileName) + 1 + 20; fwrite((const void *)&_iT, 1, 4, hFile); CCluster * _pCluster = &sInfoFile[_iI].FirstCluster; while (_pCluster) { fwrite((const void *)&_pCluster->iTaille, 1, 4, hFile); fwrite((const void *)&_pCluster->iNext, 1, 4, hFile); _pCluster = _pCluster->pNext; } } fseek(hFile, 0, SEEK_SET); fwrite((const void *)&iTailleBlock, 1, 4, hFile); fclose(hFile); hFile = NULL; ResetFAT(); return true; } //------------------------------------------------------------------------ bool CSaveBlock::Defrag() { char txt[256]; strcpy(txt, pcBlockName); strcat(txt, "DFG"); FILE * fFileTemp = fopen(txt, "wb"); int _version = PAK_VERSION + 1; fwrite((const void *)&_version, 1, 4, fFileTemp); for (int _iI = (iVersion == PAK_VERSION) ? 1 : 0; _iI < iNbFiles; _iI++) { CCluster * pCluster = &sInfoFile[_iI].FirstCluster; void * pMem = malloc(sInfoFile[_iI].iTaille); char * pcMem = (char *)pMem; int iRealSize = 0; while (pCluster) { fseek(hFile, pCluster->iNext + 4, SEEK_SET); //bug size old version if (iVersion == PAK_VERSION) { if ((iRealSize + pCluster->iTaille) > sInfoFile[_iI].iTaille) { pMem = realloc(pMem, iRealSize + pCluster->iTaille); pcMem = ((char *)pMem) + iRealSize; sInfoFile[_iI].iTaille = iRealSize + pCluster->iTaille; } } iRealSize += pCluster->iTaille; fread(pcMem, pCluster->iTaille, 1, hFile); pcMem += pCluster->iTaille; pCluster = pCluster->pNext; } fwrite(pMem, sInfoFile[_iI].iTaille, 1, fFileTemp); free(pMem); } fwrite((const void *)&_version, 1, 4, fFileTemp); int iOffset = 0; int iNbFilesTemp = (iVersion == PAK_VERSION) ? iNbFiles - 1 : iNbFiles; //-1; fwrite((const void *)&iNbFilesTemp, 1, 4, fFileTemp); for (int _iI = (iVersion == PAK_VERSION) ? 1 : 0; _iI < iNbFiles; _iI++) { fwrite((const void *)sInfoFile[_iI].pcFileName, 1, strlen((const char *)sInfoFile[_iI].pcFileName) + 1, fFileTemp); fwrite((const void *)&sInfoFile[_iI].iTaille, 1, 4, fFileTemp); int _iT = 0; fwrite((const void *)&_iT, 1, 4, fFileTemp); fwrite((const void *)&_iT, 1, 4, fFileTemp); //cluster fwrite((const void *)&sInfoFile[_iI].iTaille, 1, 4, fFileTemp); fwrite((const void *)&iOffset, 1, 4, fFileTemp); iOffset += sInfoFile[_iI].iTaille; } fseek(fFileTemp, 0, SEEK_SET); fwrite((const void *)&iOffset, 1, 4, fFileTemp); fclose(hFile); hFile = NULL; ResetFAT(); fclose(fFileTemp); DeleteFile(pcBlockName); MoveFile(txt, pcBlockName); return true; } bool CSaveBlock::ExpandNbFiles() { iNbFiles++; sInfoFile = (CInfoFile *)realloc(sInfoFile, (iNbFiles) * sizeof(CInfoFile)); memset(&sInfoFile[iNbFiles-1], 0, sizeof(CInfoFile)); return true; } //------------------------------------------------------------------------ bool CSaveBlock::Save(char * _pcFileName, void * _pDatas, int _iSize) { bool _bFound = false; CInfoFile * _pInfoFile = sInfoFile; int _iI = iNbFiles; while (_iI--) { if (!stricmp((const char *)_pInfoFile->pcFileName, (const char *)_pcFileName)) { _bFound = true; break; } _pInfoFile++; } if (!_bFound) { ExpandNbFiles(); _pInfoFile = &sInfoFile[iNbFiles-1]; _pInfoFile->Set(_pcFileName, _iSize); _pInfoFile->FirstCluster.iNext = iTailleBlock; } else { if (bReWrite) { if (!_pDatas) return false; int iNbClusters = 0; int iSizeWrite = 0; int iSize = _iSize; CCluster * _pClusterCurr = &_pInfoFile->FirstCluster; CCluster * pLastClusterCurr = NULL; while ((_pClusterCurr) && (iSize > 0)) { pLastClusterCurr = _pClusterCurr; fseek(hFile, _pClusterCurr->iNext + 4, SEEK_SET); iSizeWrite = __min(_pClusterCurr->iTaille, iSize); fwrite((const void *)_pDatas, iSizeWrite, 1, hFile); char * _pDatasTemp = (char *)_pDatas; _pDatasTemp += iSizeWrite; _pDatas = (void *)_pDatasTemp; iSize -= iSizeWrite; iNbClusters++; _pClusterCurr = _pClusterCurr->pNext; } _pInfoFile->iTaille = _iSize; if (iSize) { if (pLastClusterCurr) { pLastClusterCurr->pNext = new CCluster(iSize); pLastClusterCurr->pNext->iNext = iTailleBlock; pLastClusterCurr->pNext->pNext = NULL; iTailleBlock += iSize; fseek(hFile, 0, SEEK_END); fwrite((const void *)_pDatas, iSize, 1, hFile); _pInfoFile->iNbCluster++; } } else { if (pLastClusterCurr) { if (pLastClusterCurr->iTaille > iSizeWrite) { pLastClusterCurr->iTaille = iSizeWrite; } delete pLastClusterCurr->pNext; pLastClusterCurr->pNext = NULL; _pInfoFile->iTaille = _iSize; _pInfoFile->iNbCluster = iNbClusters; } } fseek(hFile, 0, SEEK_END); return true; } else { CCluster * _pCluster = &_pInfoFile->FirstCluster; while (_pCluster->pNext) { _pCluster = _pCluster->pNext; } _pCluster->pNext = new CCluster(_iSize); _pCluster->pNext->iNext = iTailleBlock; _pInfoFile->iTaille += _iSize; } } _pInfoFile->iNbCluster++; iTailleBlock += _iSize; if (!hFile) return false; if (_pDatas) fwrite((const void *)_pDatas, 1, _iSize, hFile); return true; } //------------------------------------------------------------------------ bool CSaveBlock::Read(char * _pcFileName, char * _pPtr) { CInfoFile * _pInfoFile = (CInfoFile *)pHachage->GetPtrWithString(_pcFileName); if (!_pInfoFile) { return false; } CCluster * _pCluster = &_pInfoFile->FirstCluster; while (_pCluster) { fseek(hFile, _pCluster->iNext + 4, SEEK_SET); fread((void *)_pPtr, _pCluster->iTaille, 1, hFile); _pPtr += _pCluster->iTaille; _pCluster = _pCluster->pNext; } return true; } //------------------------------------------------------------------------ int CSaveBlock::GetSize(char * _pcFileName) { CInfoFile * _pInfoFile = NULL; if (pHachage) { _pInfoFile = (CInfoFile *)pHachage->GetPtrWithString(_pcFileName); if (!_pInfoFile) { return -1; } } else { bool _bFound = false; CInfoFile * _pInfoFile = sInfoFile; int _iI = iNbFiles; while (_iI--) { if (!stricmp((const char *)_pInfoFile->pcFileName, (const char *)_pcFileName)) { _bFound = true; break; } _pInfoFile++; } if (!_bFound) { return -1; } } int _iTaille = 0; CCluster * _pCluster = &_pInfoFile->FirstCluster; while (_pCluster) { _iTaille += _pCluster->iTaille; _pCluster = _pCluster->pNext; } return _iTaille; } //------------------------------------------------------------------------ bool CSaveBlock::ExistFile(char * _pcFileName) { CInfoFile * _pInfoFile = NULL; if (pHachage) { _pInfoFile = (CInfoFile *)pHachage->GetPtrWithString(_pcFileName); return _pInfoFile ? true : false; } else { _pInfoFile = sInfoFile; int _iI = iNbFiles; while (_iI--) { if (!stricmp((const char *)_pInfoFile->pcFileName, (const char *)_pcFileName)) { return true; } _pInfoFile++; } return false; } }