Files
2026-06-01 12:46:52 +02:00

1275 lines
31 KiB
C++

#include "../../include/kfile/KPackingFileSystem.h"
#include "../../include/toolkit/khash.h"
#include "../../include/dump/XException.h"
#include <stdlib.h>
#include <limits>
#include "../../include/kfile/XOREn.h"
#include <algorithm>
#include <errno.h>
#include "../../include/toolkit/nsl.h"
#include "../../include/toolkit/safe_function.h"
#include "../../include/kfile/JStringLib.h"
// 2010.04.29 - prodongi
#include "../../include/kfile/KFileManager.h"
#include "../../include/compress/XZip.h"
void addToCorruptedBlock( const char *szFileName )
{
FILE *fp = NULL;
fopen_s( &fp, "corrupted.txt", "a" );
if ( fp )
{
fprintf( fp, "%s\n", szFileName );
fclose( fp );
}
}
//////////////////////////////////////////////////////////////////////////
/*
* KPackingFreeBockManager
*/
struct KPackingFreeBlockManager
{
public:
KPackingFreeBlockManager() {}
~KPackingFreeBlockManager() { Free(); }
void AddFreeBlock( size_t pos, size_t size, bool bIsReuseable )
{
if ( bIsReuseable ) m_vecFreeBlockList.push_back( KFreeBlockInfo( pos, size ) );
else m_vecDeletedBlockList.push_back( KFreeBlockInfo( pos, size ) );
CalculateOptimalBlock();
}
size_t ReserveFreeBlock( size_t size );
void CalculateOptimalBlock( bool bAllList = false );
void Free() { m_vecFreeBlockList.clear(); }
void SetBlockEnd( size_t size )
{
std::vector<KFreeBlockInfo>::iterator it = m_vecFreeBlockList.begin();
for ( ;it != m_vecFreeBlockList.end(); ++it )
{
if ( (*it).m_offset >= size )
{
m_vecFreeBlockList.erase( it, m_vecFreeBlockList.end() );
return;
}
}
}
size_t GetFreeBlockSize() const
{
size_t total_size = 0;
std::vector<KFreeBlockInfo>::const_iterator it = m_vecFreeBlockList.begin();
std::vector<KFreeBlockInfo>::const_iterator end = m_vecFreeBlockList.end();
for ( ;it != end; ++it )
{
total_size += (*it).m_size;
}
return total_size;
}
struct KFreeBlockInfo
{
KFreeBlockInfo( size_t offset, size_t size ) : m_offset(offset), m_size(size) {};
bool operator<( const KFreeBlockInfo & rh ) const
{
return m_offset < rh.m_offset;
}
size_t m_offset;
size_t m_size;
};
std::vector<KFreeBlockInfo> m_vecFreeBlockList;
std::vector<KFreeBlockInfo> m_vecDeletedBlockList;
};
size_t KPackingFreeBlockManager::ReserveFreeBlock( size_t size )
{
std::vector<KFreeBlockInfo>::iterator min_pos = m_vecFreeBlockList.end();
std::vector<KFreeBlockInfo>::iterator it = m_vecFreeBlockList.begin();
for ( ;it != m_vecFreeBlockList.end(); ++it )
{
KFreeBlockInfo info = (*it);
// 일치하는 경우
if ( info.m_size == size )
{
m_vecFreeBlockList.erase(it);
return info.m_offset;
}
if ( info.m_size < size ) continue;
if ( min_pos == m_vecFreeBlockList.end() )
{
min_pos = it;
continue;
}
if ( (*min_pos).m_size > info.m_size )
{
min_pos = it;
}
}
if ( min_pos == m_vecFreeBlockList.end() )
{
return (std::numeric_limits< size_t >::max)();
}
size_t pos = (*min_pos).m_offset;
(*min_pos).m_offset += size;
(*min_pos).m_size -= size;
return pos;
}
/*
* join blocks
*/
void KPackingFreeBlockManager::CalculateOptimalBlock( bool bAllList )
{
if ( m_vecFreeBlockList.empty() ) return;
// offset 별로 정렬
std::sort( m_vecFreeBlockList.begin(), m_vecFreeBlockList.end() );
// size summary
std::vector< KFreeBlockInfo >::iterator it;
std::vector< KFreeBlockInfo >::iterator next;
for ( it = m_vecFreeBlockList.begin(); it != m_vecFreeBlockList.end(); )
{
if ( &(*it) == &m_vecFreeBlockList.back() ) break;
next = it + 1;
if ( (*it).m_offset + (*it).m_size > (*next).m_offset )
{
assert( 0 );
MessageBox( NULL, "Packing error! (#3)", "Error", MB_OK | MB_ICONERROR );
return;
}
if ( (*it).m_offset + (*it).m_size == (*next).m_offset )
{
(*it).m_size += (*next).m_size;
it = m_vecFreeBlockList.erase( next );
if ( it != m_vecFreeBlockList.begin() ) --it;
continue;
}
++it;
}
}
//////////////////////////////////////////////////////////////////////////
/*
* KPackingFile
*/
struct KPackingFile : public KStream
{
public:
KPackingFile( KPackingFileSystem* pParent, KHFILE fp, const char *szFileName, size_t pos, size_t size, bool bIsReadOnly = true )
: m_pParent( pParent )
, m_pfp( fp )
, m_strFileName( szFileName )
, m_Offset( pos )
, m_Size( size )
, m_pBuf( NULL )
, m_BufSize( 0 )
, m_bIsClosed( false )
, m_bIsReadOnly( bIsReadOnly )
, m_CurPos( 0 )
, m_hFM( NULL )
, m_pMap( NULL )
{
// 2010.04.29 - prodongi
setClassId("KPackingFile");
}
~KPackingFile()
{
if ( m_pParent && m_bIsClosed == false )
m_pParent->close( this );
if ( m_pMap )
{
UnmapViewOfFile( m_pMap );
m_pMap = NULL;
}
if ( m_hFM )
{
CloseHandle( m_hFM );
m_hFM = NULL;
}
if ( m_pBuf )
{
delete [] m_pBuf;
m_pBuf = NULL;
}
}
bool init()
{
SYSTEM_INFO si;
GetSystemInfo(&si);
m_dwGranularity = m_Offset % si.dwAllocationGranularity;
m_hFM = ::CreateFileMapping( m_pfp, NULL, PAGE_READONLY, 0, 0, NULL );
if( m_hFM == NULL )
{
/*
char buf[1024] = "";
DWORD last_error = ::GetLastError();
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d)", buf, last_error );
MessageBox( NULL, szErr, "Rappelz", MB_OK | MB_ICONERROR );
return false;
*/
}
m_pMap = static_cast<LPBYTE>( MapViewOfFile( m_hFM, FILE_MAP_READ, 0, static_cast< DWORD >( m_Offset ) - m_dwGranularity, m_Size + m_dwGranularity) );
if( m_pMap == NULL )
{
/*
char buf[1024] = "";
DWORD last_error = ::GetLastError();
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d)", buf, last_error );
MessageBox( NULL, szErr, "Rappelz", MB_OK | MB_ICONERROR );
return false;
*/
}
return true;
}
virtual size_t Read( void *pBuf, size_t read_size );
virtual size_t Write( const void *pBuf, size_t write_size );
virtual size_t Seek( long offset, enum_seek_origin origin );
virtual size_t Tell() const;
virtual size_t GetLength() const { return getOriginalSize( m_Size ); };
virtual bool IsValid() const { return IsValidKHFile( m_pfp ); }
char* GetBuffer() const { return m_pBuf; }
const char* GetFileName() const { return m_strFileName.c_str(); }
void SetOffset( size_t offset ) { m_Offset = offset; }
const bool IsReadOnly() const { return m_bIsReadOnly; }
void SetCloseFlag( bool fFlag) { m_bIsClosed = fFlag; }
const bool GetCloseFlag() const { return m_bIsClosed; }
// 2010.04.29 - prodongi
virtual size_t getOriginalSize(size_t srcSize) const
{
if (!m_pMap)
return 0;
size_t size;
LPBYTE pMap = m_pMap + 0 + m_dwGranularity;
if (srcSize >= KFileSystem::MIN_COMPRESS_FILE_SIZE && XZip::IsCompressed(pMap, srcSize))
{
size = XZip::GetOriginalSize(pMap, srcSize);
}
else
size = m_Size;
return size;
}
virtual size_t getOriginalSize() const
{
return getOriginalSize(Size());
}
private:
KPackingFileSystem* m_pParent;
KHFILE m_pfp;
std::string m_strFileName;
bool m_bIsClosed;
char* m_pBuf;
size_t m_BufSize;
bool m_bIsReadOnly;
size_t m_Size;
size_t m_Offset;
size_t m_CurPos;
HANDLE m_hFM;
LPBYTE m_pMap;
DWORD m_dwGranularity;
};
size_t KPackingFile::Read( void *pBuf, size_t read_size )
{
if ( !IsValid() )
return 0;
if ( m_CurPos + read_size > m_Size )
read_size = m_Size - m_CurPos;
if ( read_size == 0 ) return 0;
/*
DWORD dwRead;
SetFilePointer( m_pfp, static_cast<long>(m_Offset+m_CurPos), NULL, FILE_BEGIN );
if ( ReadFile( m_pfp, pBuf, read_size, &dwRead, NULL ) )
{
m_CurPos += dwRead;
return dwRead;
}
*/
if ( !m_pMap )
return 0;
// 2010.04.29 xzip uncompress test - prodongi
/*
read_size : 압축된 사이즈
*/
size_t originalSize;
LPBYTE pMap = m_pMap + m_CurPos + m_dwGranularity;
if (read_size >= KFileSystem::MIN_COMPRESS_FILE_SIZE && XZip::IsCompressed(pMap, read_size))
{
originalSize = KFileManager::Instance().checkXZipBufferAlloc(pMap, read_size);
bool ret = XZip::Uncompress(pMap, read_size, KFileManager::Instance().m_xzipBuffer, originalSize);
if (!ret)
{
assert(0 && "failed uncompress");
return 0;
}
s_memcpy(pBuf, read_size, KFileManager::Instance().m_xzipBuffer, originalSize);
}
else
{
originalSize = read_size;
s_memcpy( pBuf, read_size, m_pMap + m_CurPos + m_dwGranularity, read_size );
}
//s_memcpy( pBuf, read_size, m_pMap + m_CurPos + m_dwGranularity, read_size );
m_CurPos += read_size;
// 2010.04.29 - prodongi
return originalSize;
//return read_size;
}
size_t KPackingFile::Write( const void *pBuf, size_t write_size )
{
if ( m_bIsReadOnly || !IsValid() ) return 0;
if ( m_CurPos + write_size > m_BufSize )
{
char *pNewBuf = new char[ m_BufSize + write_size ];
m_BufSize = m_BufSize + write_size;
if ( m_pBuf )
{
s_memcpy( pNewBuf, m_BufSize, m_pBuf, m_Size );
delete [] m_pBuf;
}
m_pBuf = pNewBuf;
}
s_memcpy( &m_pBuf[ m_CurPos ], m_BufSize-m_CurPos, pBuf, write_size );
m_CurPos += write_size;
if ( m_CurPos > m_Size ) m_Size = m_CurPos;
return write_size;
}
size_t KPackingFile::Seek( long offset, enum_seek_origin origin )
{
if ( !IsValid() ) return static_cast< size_t >( -1 );
switch(origin)
{
case seekSet:
m_CurPos = offset;
break;
case seekCur:
m_CurPos += offset;
break;
case seekEnd:
m_CurPos = m_Size;
break;
}
if ( m_CurPos < 0 ) m_CurPos = 0;
if ( m_CurPos > m_Size ) m_CurPos = m_Size;
return m_CurPos;
}
size_t KPackingFile::Tell() const
{
if ( m_pfp == NULL ) return 0;
return m_CurPos;
}
//////////////////////////////////////////////////////////////////////////
// KPackingFileSystem
//////////////////////////////////////////////////////////////////////////
bool KPackingFileSystem::KFileInfo::saveFileInfo( KHFILE fp, int* seed ) const
{
bool bRtn = true;
unsigned char file_name_len = (unsigned char)strFileName.size();
unsigned int total_size = sizeof(size_t)*2 + file_name_len + 1;
unsigned char *pBuf = new unsigned char[ total_size ];
unsigned char *p = pBuf;
int remain_buf_size = total_size;
s_memcpy( p, remain_buf_size, &file_name_len, 1 ); p += 1; remain_buf_size -= 1;
s_memcpy( p, remain_buf_size, strFileName.c_str(), file_name_len ); p += file_name_len; remain_buf_size -= file_name_len;
s_memcpy( p, remain_buf_size, &m_Offset, sizeof(size_t) ); p += sizeof(size_t); remain_buf_size -= sizeof( size_t );
s_memcpy( p, remain_buf_size, &m_Size, sizeof(size_t) ); p += sizeof(size_t); remain_buf_size -= sizeof( size_t );
for ( unsigned int i = 0; i < total_size; ++i )
{
pBuf[i] ^= XOREn::GetEncodeKeyChar( (*seed)++ );
}
DWORD dwWrite;
if ( WriteFile( fp, pBuf, total_size, &dwWrite, NULL ) )
bRtn = ( dwWrite == total_size );
else
bRtn = false;
delete [] pBuf;
return bRtn;
}
bool KPackingFileSystem::KFileInfo::loadFileInfo( KHFILE fp, int* seed, int nDataFileCount )
{
unsigned char file_name_len = 0;
DWORD dwRead;
if( !ReadFile( fp, &file_name_len, 1, &dwRead, NULL ) )
return false;
file_name_len ^= XOREn::GetEncodeKeyChar( (*seed)++ );
unsigned char szName[MAX_PATH];
if( !ReadFile( fp, szName, file_name_len, &dwRead, NULL ) || ( file_name_len != dwRead ) )
return false;
int i = 0;
for( i = 0; i < file_name_len; ++i )
{
szName[i] ^= XOREn::GetEncodeKeyChar( (*seed)++ );
}
szName[i] = 0;
size_t offset_size[2] = {0,};
unsigned char *p = (unsigned char *)offset_size;
if ( !ReadFile( fp, offset_size, sizeof(size_t) * 2, &dwRead, NULL ) || ( sizeof(size_t) * 2 != dwRead ) )
return false;
for ( int i = 0; i < sizeof(size_t)*2; ++i )
{
*(p++) ^= XOREn::GetEncodeKeyChar( (*seed)++ );
}
strFileName = reinterpret_cast< const char* >( szName );
m_Offset = offset_size[0];
m_Size = offset_size[1];
m_Index = static_cast< unsigned short >( KPackingFileSystem::getFileHashKey( (const char*)szName, nDataFileCount ) );
return true;
}
KPackingFileSystem::KPackingFileSystem( int nDataFileCount, int nMaxThreadCount, bool bIsReadOnly ) : m_bIsReadOnly( bIsReadOnly )
{
m_nDataFileCount = nDataFileCount;
m_nMaxThreadCount = nMaxThreadCount;
m_nDataFileSize = new size_t[m_nDataFileCount];
for ( size_t i = 0; i < m_nDataFileCount; ++i )
{
m_nDataFileSize[i] = 0;
}
m_hsIndexFileList.clear();
m_pFreeBlockManager = NULL;
}
KPackingFileSystem::~KPackingFileSystem()
{
// legacy 코드와의 하위호환성을 위해 소멸자에서는 save 파일을 저장하지 않는다.
// by Testors
DeInit( false );
//DeInit();
if( m_pFreeBlockManager != NULL )
{
delete [] m_pFreeBlockManager;
m_pFreeBlockManager = NULL;
}
delete [] m_nDataFileSize;
}
bool KPackingFileSystem::Init( const char* szIndexFileName, const char* szDataFileName )
{
m_strIndexFileName = szIndexFileName;
m_strDataFileName = szDataFileName;
bool bFullIndexPath = JStringLib::IsFullPath( m_strIndexFileName.c_str() );
bool bFullDataPath = JStringLib::IsFullPath( m_strDataFileName.c_str() );
std::string strDirectory = m_strIndexFileName;
JStringLib::GetFilePathFromFile( strDirectory );
m_strCurDirectory = strDirectory;
if ( bFullIndexPath ) JStringLib::GetFileNameFormPath( m_strIndexFileName );
if ( bFullDataPath ) JStringLib::GetFileNameFormPath( m_strDataFileName );
// load index, del_list file
if ( !LoadIndexFile() ) return false;
CalcurateFreeBlock();
return true;
}
KPackingFileSystem::KFileHandleMap::~KFileHandleMap()
{
for ( int i = 0; i < maxHandleCount; ++i )
{
if ( handle[i] )
{
::FlushFileBuffers( handle[i] );
::CloseHandle( handle[i] );
}
}
delete [] handle;
}
bool KPackingFileSystem::DeInit( bool bSaveIndexFile )
{
if( bSaveIndexFile )
{
if ( !SaveIndexFile() ) return false;
}
if ( m_hsIndexFileList.size() > 0 ) m_hsIndexFileList.clear();
{
THREAD_SYNCRONIZE( m_lckDataFileHandle );
for ( std::vector< KFileHandleMap* >::iterator it = m_vecFileHandle.begin(); it != m_vecFileHandle.end(); ++it )
delete *it;
m_vecFileHandle.clear();
}
return true;
}
bool KPackingFileSystem::Flush( bool bSaveIndexFile )
{
bool ret = false;
if( bSaveIndexFile == true )
{
ret = SaveIndexFile();
}
{
THREAD_SYNCRONIZE( m_lckDataFileHandle );
for ( std::vector< KFileHandleMap* >::iterator it = m_vecFileHandle.begin(); it != m_vecFileHandle.end(); ++it )
delete *it;
m_vecFileHandle.clear();
}
return ret;
}
void KPackingFileSystem::doEachFile( FileHandler & handler )
{
THREAD_SYNCRONIZE( m_lckDataFileHandle );
KHash< KFileInfo, hashPr_string_nocase >::node* _node = NULL;
bool bResult = m_hsIndexFileList.get_first_node( _node );
while ( bResult )
{
if ( !handler.onFile( _node->key.m_pStr ) ) break;
bResult = m_hsIndexFileList.get_next_node( _node );
}
}
KHFILE KPackingFileSystem::getDataFileHandle( const char *szFileName )
{
THREAD_SYNCRONIZE( m_lckDataFileHandle );
KHFILE fp = NULL;
int thread_id = GetCurrentThreadId();
std::vector< KFileHandleMap* >::iterator it;
for ( it = m_vecFileHandle.begin(); it != m_vecFileHandle.end(); ++it )
{
if ( (*it)->threadId == thread_id )
{
KFileHandleMap* h = *it;
m_vecFileHandle.erase( it );
m_vecFileHandle.push_back( h );
it = m_vecFileHandle.end()-1;
break;
}
}
if ( it == m_vecFileHandle.end() )
{
m_vecFileHandle.push_back( new KFileHandleMap( static_cast< int >( m_nDataFileCount ), thread_id ) );
while ( m_vecFileHandle.size() > m_nMaxThreadCount )
{
delete m_vecFileHandle.front();
m_vecFileHandle.erase( m_vecFileHandle.begin() );
}
}
size_t hash_key = getFileHashKey( szFileName, m_nDataFileCount );
KFileHandleMap* p = m_vecFileHandle.back();
if ( p->handle[ hash_key ] != NULL )
return p->handle[ hash_key ];
std::string strFullPath = m_strCurDirectory + getDataFileName( m_strDataFileName.c_str(), hash_key );
#ifdef _DEBUG
OutputDebugString( strFullPath.c_str() );
OutputDebugString( "\n" );
#endif
DWORD dwSwitch = GENERIC_READ | (m_bIsReadOnly ? 0 : GENERIC_WRITE );
fp = ::CreateFile( strFullPath.c_str(), dwSwitch, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
if ( !IsValidKHFile( fp ) )
{
if( m_bIsReadOnly == false )
{
fp = ::CreateFile( strFullPath.c_str(), dwSwitch, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
}
}
if( !IsValidKHFile( fp ) )
{
DWORD last_error = GetLastError();
char buf[1024] = "";
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d)", buf, last_error );
MessageBox( NULL, buf, "Rappelz", MB_OK | MB_ICONERROR );
assert( false );
return NULL;
}
p->handle[ hash_key ] = fp;
CalcurateDataFileSize();
return fp;
}
KStream* KPackingFileSystem::open( const char* szFileName, ACCESS_MODE access_mode )
{
KHFILE fp = getDataFileHandle( szFileName );
if ( !IsValidKHFile( fp ) )
{
return NULL;
}
std::string strName = szFileName;
bool bIsReadOnly = false;
if ( access_mode == KPackingFileSystem::READ_ONLY ) bIsReadOnly = true;
KFileInfo info;
if ( m_hsIndexFileList.lookup( strName.c_str(), info ) )
{
if ( !bIsReadOnly )
{
assert( 0 );
}
KPackingFile* pFile = new KPackingFile( this, fp, strName.c_str(), info.m_Offset, info.m_Size, bIsReadOnly );
if( pFile != NULL )
{
if( pFile->init() == false )
{
delete pFile;
pFile = NULL;
}
}
return pFile;
}
else
{
// 쓰고 있는 파일을 또 열려 했을경우 NULL
if ( m_hsIndexWritingFileList.has( strName ) )
{
return NULL;
}
if ( bIsReadOnly )
{
return NULL;
}
KPackingFile *pFile = new KPackingFile( this, fp, strName.c_str(), 0, 0, false );
if( pFile != NULL )
{
if( pFile->init() == false )
{
delete pFile;
pFile = NULL;
}
else
{
m_hsIndexWritingFileList.add( strName, pFile );
}
}
return pFile;
}
}
void KPackingFileSystem::CalcurateDataFileSize()
{
for ( size_t i = 0; i < m_nDataFileCount; ++i ) m_nDataFileSize[i] = 0;
bool bResult;
KHash< KFileInfo, hashPr_string_nocase >::node* _node = NULL;
bResult = m_hsIndexFileList.get_first_node( _node );
while ( bResult )
{
if ( _node->value.m_Offset + _node->value.m_Size > m_nDataFileSize[ _node->value.m_Index ] )
{
m_nDataFileSize[ _node->value.m_Index ] = _node->value.m_Offset + _node->value.m_Size;
}
bResult = m_hsIndexFileList.get_next_node( _node );
}
for ( size_t i = 0; i < m_nDataFileCount; ++i )
{
m_pFreeBlockManager[i].SetBlockEnd( m_nDataFileSize[i] );
}
//m_nDataFileSize = nDataFileSize;
}
bool KPackingFileSystem::close( KStream *pStream )
{
KPackingFile *pFile = static_cast< KPackingFile * >( pStream );
if ( !pFile )
{
return false;
}
if( close( pFile, pFile->GetBuffer(), pFile->Size() ) == false )
{
return false;
}
return true;
}
bool KPackingFileSystem::close( class KStream* pStream, const void* pBuffer, size_t size )
{
KPackingFile *pFile = static_cast< KPackingFile * >( pStream );
if ( !pFile )
{
return false;
}
if ( pFile->IsReadOnly() )
{
return true;
}
m_hsIndexWritingFileList.erase( pFile->GetFileName() );
// 0바이트는 쓰지 말자
if( size == 0 )
{
return true;
}
size_t hash_key = getFileHashKey( pFile->GetFileName(), m_nDataFileCount );
KHFILE fp = getDataFileHandle( pFile->GetFileName() );
if ( !IsValidKHFile( fp ) )
{
return false;
}
// 일단 기존 파일은 지운다
deleteFile( pFile->GetFileName() );
pFile->SetCloseFlag( true );
// 빈블럭 있으면 빈블럭에 삽입
size_t free_pos = m_pFreeBlockManager[hash_key].ReserveFreeBlock( size );
if ( free_pos != (std::numeric_limits< size_t >::max)() )
{
pFile->SetOffset( free_pos );
DWORD dwRet = SetFilePointer( fp, static_cast< LONG >( free_pos ), NULL, FILE_BEGIN );
if( dwRet == INVALID_SET_FILE_POINTER )
{
DWORD last_error = ::GetLastError();
char buf[1024] = "";
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d)", buf, last_error );
MessageBox( NULL, buf, "Rappelz", MB_OK | MB_ICONERROR );
return false;
}
DWORD dwWrite;
if ( WriteFile( fp, pBuffer, static_cast< DWORD >( size ), &dwWrite, NULL ) == FALSE )
{
DWORD last_error = ::GetLastError();
char buf[1024] = "";
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d)", buf, last_error );
MessageBox( NULL, buf, "Rappelz", MB_OK | MB_ICONERROR );
return false;
}
KFileInfo info( pFile->GetFileName(), size, free_pos, static_cast< unsigned short >( hash_key ) );
m_hsIndexFileList.add( pFile->GetFileName(), info );
return true;
}
// 맨 뒤에 삽입
KFileInfo info( pFile->GetFileName(), size, m_nDataFileSize[hash_key], static_cast< unsigned short >( hash_key ) );
m_hsIndexFileList.add( pFile->GetFileName(), info );
// { FAT32 에서는 4G 이상의 파일은 만들 수 없다.
__int64 data_size = m_nDataFileSize[hash_key];
data_size += size;
if ( (__int64)(std::numeric_limits< DWORD >::max)() < data_size )
{
return false;
}
// }
DWORD dwRet = SetFilePointer( fp, static_cast< LONG >( m_nDataFileSize[hash_key] ), NULL, FILE_BEGIN );
if( dwRet == INVALID_SET_FILE_POINTER )
{
DWORD last_error = ::GetLastError();
char buf[1024] = "";
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d)", buf, last_error );
MessageBox( NULL, buf, "Rappelz", MB_OK | MB_ICONERROR );
return false;
}
size_t write_size = size;
const char* write_buffer = static_cast< const char* >( pBuffer );
while( write_size > 0 )
{
DWORD request_size = write_size;
if( request_size > JStringLib::MAX_FILE_WRITE_SIZE )
{
request_size = JStringLib::MAX_FILE_WRITE_SIZE;
}
DWORD written = 0;
if( WriteFile( fp, write_buffer, request_size, &written, NULL ) == FALSE )
{
DWORD last_error = ::GetLastError();
char buf[1024] = "";
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d)", buf, last_error );
MessageBox( NULL, buf, "Rappelz", MB_OK | MB_ICONERROR );
return false;
}
//버퍼 이동
write_buffer += written;
if( write_size > written )
{
write_size -= written;
}
else
{
assert( write_size == written );
write_size = 0;
}
}
FlushFileBuffers( fp );
CalcurateDataFileSize();
return true;
}
size_t KPackingFileSystem::getFileSize( const char* szFileName ) const
{
KFileInfo info;
if ( m_hsIndexFileList.lookup( szFileName, info ) ) return info.m_Size;
return 0;
}
unsigned __int64 KPackingFileSystem::getDataFileSize()
{
unsigned __int64 total_size = 0;
KHash< KFileInfo, hashPr_string_nocase >::node* _node = NULL;
bool bResult = m_hsIndexFileList.get_first_node( _node );
while ( bResult )
{
total_size += _node->value.m_Size;
bResult = m_hsIndexFileList.get_next_node( _node );
}
return total_size;
}
unsigned __int64 KPackingFileSystem::getFreeSize() const
{
unsigned __int64 total_free_size = 0;
if( m_pFreeBlockManager != NULL )
{
for ( size_t i = 0; i < m_nDataFileCount; ++i )
{
total_free_size += m_pFreeBlockManager[i].GetFreeBlockSize();
}
}
return total_free_size;
}
bool KPackingFileSystem::changeDirectory( const char *szDirectory )
{
return true;
}
bool KPackingFileSystem::makeDirectory( const char *szDirectory )
{
m_strCurDirectory = szDirectory;
return !!(::CreateDirectory( szDirectory, NULL ));
}
bool KPackingFileSystem::deleteDirectory( const char *szDirectory )
{
// 폴더에 있는 기존 파일 모두 삭제
std::string strFile = szDirectory;
std::string strTemp = strFile.substr( strFile.size()-1, 1 );
if ( ::_stricmp( strTemp.c_str(), "/" ) != 0 )
strFile += "/";
strFile += "*.*";
WIN32_FIND_DATA data;
HANDLE hSearch = FindFirstFile( strFile.c_str(), &data );
if ( hSearch == INVALID_HANDLE_VALUE )
return !!(RemoveDirectory( szDirectory ));
bool bResult = true;
while ( bResult )
{
// hash 에서 삭제
KFileInfo info;
if ( m_hsIndexFileList.lookup(data.cFileName, info) )
m_hsIndexFileList.erase(data.cFileName);
bResult = (FindNextFile( hSearch, &data ) != FALSE);
}
return !!(RemoveDirectory( szDirectory ));
}
bool KPackingFileSystem::deleteFile( const char *szFile )
{
KFileInfo info;
if ( m_hsIndexFileList.lookup( szFile, info ) )
{
// add del_list
m_pFreeBlockManager[getFileHashKey( szFile, m_nDataFileCount )].AddFreeBlock( info.m_Offset, info.m_Size, false );
// delete in index list
m_hsIndexFileList.erase( szFile );
std::string strFullPath = m_strCurDirectory + szFile;
JStringLib::ReplaceString( strFullPath, "/", "\\" );
return true;
}
return false;
}
/*
* load index file
*/
bool KPackingFileSystem::LoadIndexFile()
{
std::string strIndexFile = m_strCurDirectory + m_strIndexFileName;
KHFILE pFile;
pFile = ::CreateFile( strIndexFile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
if ( !IsValidKHFile( pFile ) )
{
if( m_bIsReadOnly == false )
{
return true;
}
char buf[1024] = "";
DWORD last_error = ::GetLastError();
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d) - %s", buf, last_error, strIndexFile.c_str() );
MessageBox( NULL, szErr, "Rappelz", MB_OK | MB_ICONERROR );
return false;
}
int seed = 0;
while ( SetFilePointer( pFile, 0, NULL, FILE_CURRENT ) < GetFileSize( pFile, NULL ) )
{
KFileInfo info;
if( info.loadFileInfo( pFile, &seed, m_nDataFileCount ) == false )
{
break;
}
m_hsIndexFileList.add( info.strFileName, info );
}
CloseHandle( pFile );
return true;
}
/*
* load del_list file
*/
void KPackingFileSystem::CalcurateFreeBlock()
{
if( m_pFreeBlockManager != NULL )
{
delete [] m_pFreeBlockManager;
m_pFreeBlockManager = NULL;
}
m_pFreeBlockManager = new KPackingFreeBlockManager[m_nDataFileCount];
bool bResult;
KHash< KFileInfo, hashPr_string_nocase >::node* _node = NULL;
std::vector< KFileInfo > *vFileList = new std::vector< KFileInfo >[m_nDataFileCount];
// 파일 리스트를 벡터에 복사
bResult = m_hsIndexFileList.get_first_node( _node );
while ( bResult )
{
vFileList[_node->value.m_Index].push_back( _node->value );
bResult = m_hsIndexFileList.get_next_node( _node );
}
for ( size_t i = 0; i < m_nDataFileCount; ++i )
{
// 시작위치 기준으로 정렬
std::sort( vFileList[i].begin(), vFileList[i].end() );
// 빈 공간 찾아서 KFreeBlockManager 에 삽입
std::vector< KFileInfo >::iterator it;
std::vector< KFileInfo >::iterator next;
for ( it = vFileList[i].begin(); it != vFileList[i].end(); ++it )
{
if ( it == vFileList[i].begin() )
{
if ( (*it).m_Offset != 0 ) m_pFreeBlockManager[i].AddFreeBlock( 0, (*it).m_Offset, true );
}
next = it + 1;
if ( next == vFileList[i].end() ) break;
if ( (*it).m_Offset + (*it).m_Size > (*next).m_Offset )
{
addToCorruptedBlock( (*it).strFileName.c_str() );
addToCorruptedBlock( (*next).strFileName.c_str() );
}
size_t offset = (*it).m_Offset + (*it).m_Size;
if ( offset < (*next).m_Offset ) m_pFreeBlockManager[i].AddFreeBlock( offset, (*next).m_Offset - offset, true );
}
}
delete [] vFileList;
}
#include <windows.h>
/*
* save index file
*/
bool KPackingFileSystem::SaveIndexFile()
{
const std::string strIndexFile = m_strCurDirectory + m_strIndexFileName;
const std::string strOldIndexFile = m_strCurDirectory + m_strIndexFileName + ".old";
const std::string strNewIndexFile = m_strCurDirectory + m_strIndexFileName + ".new";
if ( m_hsIndexFileList.size() <= 0 ) return false;
SetFileAttributes( strNewIndexFile.c_str(), FILE_ATTRIBUTE_NORMAL );
DeleteFile( strNewIndexFile.c_str() );
KHFILE pFile;
pFile = ::CreateFile( strNewIndexFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
if ( !IsValidKHFile( pFile) )
{
char buf[1024] = "";
DWORD last_error = ::GetLastError();
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d) - %s", buf, last_error, strNewIndexFile.c_str() );
MessageBox( NULL, szErr, "Rappelz", MB_OK | MB_ICONERROR );
assert( 0 );
return false;
}
// 모두 새로 저장한다.
bool bResult;
KHash< KFileInfo, hashPr_string_nocase >::node* _node = NULL;
int seed = 0;
bResult = m_hsIndexFileList.get_first_node( _node );
while ( bResult )
{
if( _node->value.saveFileInfo( pFile, &seed ) == false )
{
char buf[1024] = "";
DWORD last_error = ::GetLastError();
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d) - %s", buf, last_error, strNewIndexFile.c_str() );
MessageBox( NULL, szErr, "Rappelz", MB_OK | MB_ICONERROR );
CloseHandle( pFile );
DeleteFile( strNewIndexFile.c_str() );
return false;
}
bResult = m_hsIndexFileList.get_next_node( _node );
}
FlushFileBuffers( pFile );
CloseHandle( pFile );
// old 파일 삭제
SetFileAttributes( strOldIndexFile.c_str(), FILE_ATTRIBUTE_NORMAL );
DeleteFile( strOldIndexFile.c_str() );
// 백업
MoveFile( strIndexFile.c_str(), strOldIndexFile.c_str() );
// 적용
if( ::MoveFile( strNewIndexFile.c_str(), strIndexFile.c_str() ) != TRUE )
{
char buf[1024] = "";
DWORD last_error = ::GetLastError();
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, 0, buf, _countof(buf), NULL );
char szErr[1024] = { 0, };
s_sprintf( szErr, _countof( szErr ), "%s(%d) - %s", buf, last_error, strNewIndexFile.c_str() );
MessageBox( NULL, szErr, "Rappelz", MB_OK | MB_ICONERROR );
// 복구
MoveFile( strOldIndexFile.c_str(), strIndexFile.c_str() );
DeleteFile( strNewIndexFile.c_str() );
return false;
}
return true;
}
const size_t KPackingFileSystem::GetIndexNameList( std::vector<std::string>& vecNameList )
{
if ( m_hsIndexFileList.size() <= 0 )
return (std::numeric_limits<size_t>::max)();
KHash< KPackingFileSystem::KFileInfo, hashPr_string_nocase >::node* _node = NULL;
bool bResult = m_hsIndexFileList.get_first_node( _node );
while ( bResult )
{
KPackingFileSystem::KFileInfo info = _node->value;
if ( info.m_Size > 0 )
vecNameList.push_back( _node->key.m_pStr );
bResult = m_hsIndexFileList.get_next_node( _node );
}
return vecNameList.size();
}
size_t KPackingFileSystem::getFileHashKey( const char* szFileName, int nDataFileCount )
{
int key = 0;
for ( int i = 0; szFileName[i]; ++i )
{
int tmp = szFileName[i];
if ( tmp >= 'A' && tmp <= 'Z' ) { tmp -= ( 'A' - 'a' ); }
key = key * 32 - key + tmp;
}
if ( key < 0 ) key = 0 - key;
return key % nDataFileCount;
}
std::string KPackingFileSystem::getDataFileName( const char* szDataFileName, size_t hash_key )
{
char buf[256] = "";
s_sprintf( buf, _countof( buf ), "%s.%03d", szDataFileName, hash_key+1 );
return buf;
}