commit 13f100fbfcaab43e61325e80ceb38e7dca92d054 Author: NovaRain Date: Sat Apr 1 20:46:51 2023 +0800 Initial commit: the original DAT2 v2.32 diff --git a/dat2.exe b/dat2.exe new file mode 100644 index 0000000..0b31f3d Binary files /dev/null and b/dat2.exe differ diff --git a/src/DatFile1.cpp b/src/DatFile1.cpp new file mode 100644 index 0000000..e6981b6 --- /dev/null +++ b/src/DatFile1.cpp @@ -0,0 +1,991 @@ +// DatFile1.cpp : implementation file +// + +#include "stdafx.h" +#include "DatFile1.h" +#include "LZ77C.h" +#include "Util.h" + + +#if defined(_UNICODE) || defined(UNICODE) +#error UNICODE not supported +#endif + + +// CFileList1::CFileTreeItem +CFileList1::CFileTreeItem::CFileTreeItem() +{ +} + +CFileList1::CFileTreeItem::CFileTreeItem(const CFileList1::CFileTreeItem& item) +{ + m_strDirectoryName = item.m_strDirectoryName; + m_FileList.Copy(item.m_FileList); +} + +CFileList1::CFileTreeItem& CFileList1::CFileTreeItem::operator = (const CFileList1::CFileTreeItem& item) +{ + if (&item != this) { + m_strDirectoryName = item.m_strDirectoryName; + m_FileList.Copy(item.m_FileList); + } + + return (*this); +} + +// CFileList1 +IMPLEMENT_DYNAMIC(CFileList1, CObject) + +CFileList1::CFileList1() +{ +} + +CFileList1::~CFileList1() +{ +} + +void CFileList1::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) { + // Directories count + DWORD dwDirCount = DWORD(m_FileTree.GetSize()); + WriteMSBDWord(ar, dwDirCount); + + // Unknown fields + WriteMSBDWord(ar, dwDirCount); + WriteMSBDWord(ar, 0); + WriteMSBDWord(ar, 0); + + // Directories names + CString strDirName; + + for(DWORD i = 0; i < dwDirCount; i++) { + strDirName = m_FileTree[i].m_strDirectoryName; + + if (strDirName.IsEmpty()) { + strDirName = "."; + } + + WriteString(ar, strDirName); + } + + // Directories descriptors + for(DWORD i = 0; i < dwDirCount; i++) { + // FileCount + DWORD dwFileCount = DWORD(m_FileTree[i].m_FileList.GetSize()); + + WriteMSBDWord(ar, dwFileCount); + + // Unknown fields + WriteMSBDWord(ar, dwFileCount); + WriteMSBDWord(ar, 16); + WriteMSBDWord(ar, 0); + + // File descriptors + CString strFileName; + CFileDescriptorBase descriptor; + DWORD dwFlags; + DWORD dwPackedFileLength; + + for(DWORD j = 0; j < dwFileCount; j++) { + descriptor = m_FileTree[i].m_FileList[j]; + strFileName = ExtractFileName(descriptor.m_strFileName); + + WriteString(ar, strFileName); + + if (descriptor.m_nType == 1) { + dwFlags = 0x40; + dwPackedFileLength = descriptor.m_dwPackedFileLength; + } + else { + dwFlags = 0x20; + dwPackedFileLength = 0; + } + + WriteMSBDWord(ar, dwFlags); + + WriteMSBDWord(ar, descriptor.m_dwOffset); + WriteMSBDWord(ar, descriptor.m_dwFileLength); + WriteMSBDWord(ar, dwPackedFileLength); + } + } + } + else { + m_FileTree.RemoveAll(); + + // Directories count + DWORD dwDirCount; + + if (ReadMSBDWord(ar, dwDirCount) != sizeof(DWORD)) { + AfxThrowUserException(); + } + + // Unknown fields + DWORD dwUnknown; + + for(DWORD u = 0; u < 3; u++) { + if (ReadMSBDWord(ar, dwUnknown) != sizeof(DWORD)) { + AfxThrowUserException(); + } + } + + // Directories names + CFileTreeItem directory; + CString strDirName; + + + for(DWORD i = 0; i < dwDirCount; i++) { + if (!ReadString(ar, strDirName)) { + AfxThrowUserException(); + } + + if (strDirName == ".") { + strDirName = ""; + } + + directory.m_strDirectoryName = strDirName; + m_FileTree.Add(directory); + } + + // Directories descriptors + for(DWORD i = 0; i < dwDirCount; i++) { + // FileCount + DWORD dwFileCount; + + if (ReadMSBDWord(ar, dwFileCount) != sizeof(DWORD)) { + AfxThrowUserException(); + } + + // Unknown fields + for(DWORD u = 0; u < 3; u++) { + if (ReadMSBDWord(ar, dwUnknown) != sizeof(DWORD)) { + AfxThrowUserException(); + } + } + + // File descriptors + CFileDescriptorBase descriptor; + DWORD dwFlags; + + for(DWORD j = 0; j < dwFileCount; j++) { + if (!ReadString(ar, descriptor.m_strFileName)) { + AfxThrowUserException(); + } + + if (ReadMSBDWord(ar, dwFlags) != sizeof(DWORD)) { + AfxThrowUserException(); + } + + if (ReadMSBDWord(ar, descriptor.m_dwOffset) != sizeof(DWORD)) { + AfxThrowUserException(); + } + + if (ReadMSBDWord(ar, descriptor.m_dwFileLength) != sizeof(DWORD)) { + AfxThrowUserException(); + } + + if (ReadMSBDWord(ar, descriptor.m_dwPackedFileLength) != sizeof(DWORD)) { + AfxThrowUserException(); + } + + if (dwFlags == 0x40) { + descriptor.m_nType = 1; + } + else { + descriptor.m_nType = 0; + descriptor.m_dwPackedFileLength = descriptor.m_dwFileLength; + } + + if (!m_FileTree[i].m_strDirectoryName.IsEmpty()) { + descriptor.m_strFileName = m_FileTree[i].m_strDirectoryName + "\\" + descriptor.m_strFileName; + } + + m_FileTree[i].m_FileList.Add(descriptor); + } + } + } +} + +INT_PTR CFileList1::GetSize() const +{ + INT_PTR nResult = 0; + + for(INT_PTR i = 0; i < m_FileTree.GetSize(); i++) { + nResult += m_FileTree[i].m_FileList.GetSize(); + } + + return nResult; +} + +DWORD CFileList1::GetLength() +{ + DWORD dwResult = sizeof(DWORD) * 4; + CString strDirectoryName; + + for(INT_PTR i = 0; i < m_FileTree.GetSize(); i++) { + strDirectoryName = m_FileTree[i].m_strDirectoryName; + + if (strDirectoryName.IsEmpty()) { + strDirectoryName = "."; + } + + dwResult += strDirectoryName.GetLength() + 1; + dwResult += sizeof(DWORD) * 4; + + for(INT_PTR j = 0; j < m_FileTree[i].m_FileList.GetSize(); j++) { + dwResult += ExtractFileName(m_FileTree[i].m_FileList[j].m_strFileName).GetLength() + 1; + dwResult += sizeof(DWORD) * 4; + } + } + + return dwResult; +} + + +INT_PTR CFileList1::Add(const CFileDescriptorBase& newElement) +{ + CString strDirectoryName = ExtractFilePath(newElement.m_strFileName, FALSE); + CString strFileName = ExtractFileName(newElement.m_strFileName); + + if (!strDirectoryName.IsEmpty()) { + strDirectoryName = strDirectoryName.Left(strDirectoryName.GetLength() - 1); + } + + // ========= Directory ========= + INT_PTR nDirectoryIndex; + CFileTreeItem directory; + + directory.m_strDirectoryName = strDirectoryName; + + // No elements + if (m_FileTree.IsEmpty()) { + nDirectoryIndex = m_FileTree.Add(directory); + } + else if (MakeLower(strDirectoryName) < + MakeLower(m_FileTree[0].m_strDirectoryName)) { + // Less than first + m_FileTree.InsertAt(0, directory); + nDirectoryIndex = 0; + } + else if (MakeLower(strDirectoryName) > + MakeLower(m_FileTree[m_FileTree.GetUpperBound()].m_strDirectoryName)) { + // Greater than last + nDirectoryIndex = m_FileTree.Add(directory); + } + else if (MakeLower(strDirectoryName) == + MakeLower(m_FileTree[0].m_strDirectoryName)) { + // Equal first + nDirectoryIndex = 0; + } + else if (MakeLower(strDirectoryName) == + MakeLower(m_FileTree[m_FileTree.GetUpperBound()].m_strDirectoryName)) { + // Equal last + nDirectoryIndex = m_FileTree.GetUpperBound(); + } + else { + // Other + BOOL bNeedInsert = TRUE; + INT_PTR nLeft = 0; + INT_PTR nRight = m_FileTree.GetUpperBound(); + INT_PTR nCurrent; + + while(nRight - nLeft > 1) { + nCurrent = (nRight + nLeft) >> 1; // divide by 2 + + if (MakeLower(strDirectoryName) == MakeLower(m_FileTree[nCurrent].m_strDirectoryName)) { + bNeedInsert = FALSE; + break; + } + else if (MakeLower(strDirectoryName) < MakeLower(m_FileTree[nCurrent].m_strDirectoryName)) { + nRight = nCurrent; + } + else { + nLeft = nCurrent; + } + } + + if (bNeedInsert) { + m_FileTree.InsertAt(nLeft + 1, directory); + nDirectoryIndex = nLeft + 1; + } + else { + nDirectoryIndex = nCurrent; + } + } + + // ========= File ========= + INT_PTR nFileIndex; + + + if (m_FileTree[nDirectoryIndex].m_FileList.IsEmpty()) { + // No elements + nFileIndex = m_FileTree[nDirectoryIndex].m_FileList.Add(newElement); + } + else if (MakeLower(newElement.m_strFileName) < + MakeLower(m_FileTree[nDirectoryIndex].m_FileList[0].m_strFileName)) { + // Less than first + m_FileTree[nDirectoryIndex].m_FileList.InsertAt(0, newElement); + nFileIndex = 0; + } + else if (MakeLower(newElement.m_strFileName) > + MakeLower(m_FileTree[nDirectoryIndex].m_FileList[m_FileTree[nDirectoryIndex].m_FileList.GetUpperBound()].m_strFileName)) { + // Greater than last + nFileIndex = m_FileTree[nDirectoryIndex].m_FileList.Add(newElement); + } + else if (MakeLower(newElement.m_strFileName) == + MakeLower(m_FileTree[nDirectoryIndex].m_FileList[0].m_strFileName)) { + // Equal first + m_FileTree[nDirectoryIndex].m_FileList[0] = newElement; + nFileIndex = 0; + } + else if (MakeLower(newElement.m_strFileName) == + MakeLower(m_FileTree[nDirectoryIndex].m_FileList[m_FileTree[nDirectoryIndex].m_FileList.GetUpperBound()].m_strFileName)) { + // Equal last + m_FileTree[nDirectoryIndex].m_FileList[m_FileTree[nDirectoryIndex].m_FileList.GetUpperBound()] = newElement; + nFileIndex = m_FileTree[nDirectoryIndex].m_FileList.GetUpperBound(); + } + else { + // Other + BOOL bNeedInsert = TRUE; + INT_PTR nLeft = 0; + INT_PTR nRight = m_FileTree[nDirectoryIndex].m_FileList.GetUpperBound(); + INT_PTR nCurrent; + + while(nRight - nLeft > 1) { + nCurrent = (nRight + nLeft) >> 1; // divide by 2 + + if (MakeLower(newElement.m_strFileName) == MakeLower(m_FileTree[nDirectoryIndex].m_FileList[nCurrent].m_strFileName)) { + m_FileTree[nDirectoryIndex].m_FileList[nCurrent] = newElement; + bNeedInsert = FALSE; + break; + } + else if (MakeLower(newElement.m_strFileName) < MakeLower(m_FileTree[nDirectoryIndex].m_FileList[nCurrent].m_strFileName)) { + nRight = nCurrent; + } + else { + nLeft = nCurrent; + } + } + + if (bNeedInsert) { + m_FileTree[nDirectoryIndex].m_FileList.InsertAt(nLeft + 1, newElement); + nFileIndex = nLeft + 1; + } + else { + nFileIndex = nCurrent; + } + } + + INT_PTR nResult = 0; + + for(INT_PTR i = 0; i < nDirectoryIndex; i++) { + nResult += m_FileTree[nDirectoryIndex].m_FileList.GetSize(); + } + + nResult += nFileIndex; + + return nResult; +} + +void CFileList1::RemoveAt(INT_PTR nIndex) +{ + ASSERT(nIndex >= 0); + ASSERT(nIndex < GetSize()); + + for(INT_PTR nDirectoryIndex = 0; nDirectoryIndex < m_FileTree.GetSize(); nDirectoryIndex++) { + if (nIndex < m_FileTree[nDirectoryIndex].m_FileList.GetSize()) { + m_FileTree[nDirectoryIndex].m_FileList.RemoveAt(nIndex); + + if (m_FileTree[nDirectoryIndex].m_FileList.IsEmpty()) { + m_FileTree.RemoveAt(nDirectoryIndex); + } + + break; + } + else { + nIndex -= m_FileTree[nDirectoryIndex].m_FileList.GetSize(); + } + } +} + +CFileDescriptorBase& CFileList1::operator [] (INT_PTR nIndex) +{ + ASSERT(nIndex >= 0); + ASSERT(nIndex < GetSize()); + + for(INT_PTR nDirectoryIndex = 0; nDirectoryIndex < m_FileTree.GetSize(); nDirectoryIndex++) { + if (nIndex < m_FileTree[nDirectoryIndex].m_FileList.GetSize()) { + return m_FileTree[nDirectoryIndex].m_FileList[nIndex]; + } + else { + nIndex -= m_FileTree[nDirectoryIndex].m_FileList.GetSize(); + } + } + + // Fake return (avoid warning) + return m_FileTree[0].m_FileList[0]; +} + +CFileDescriptorBase CFileList1::operator [] (INT_PTR nIndex) const +{ + ASSERT(nIndex >= 0); + ASSERT(nIndex < GetSize()); + + for(INT_PTR nDirectoryIndex = 0; nDirectoryIndex < m_FileTree.GetSize(); nDirectoryIndex++) { + if (nIndex < m_FileTree[nDirectoryIndex].m_FileList.GetSize()) { + return m_FileTree[nDirectoryIndex].m_FileList[nIndex]; + } + else { + nIndex -= m_FileTree[nDirectoryIndex].m_FileList.GetSize(); + } + } + + // Fake return (avoid warning) + return m_FileTree[0].m_FileList[0]; +} + +#ifdef _DEBUG +void CFileList1::AssertValid() const +{ + CObject::AssertValid(); + m_FileTree.AssertValid(); + + for(INT_PTR i = 0; i < GetSize(); i++) { + (operator [] (i)).AssertValid(); + } +} + +template<> +void AFXAPI DumpElements(CDumpContext& dc, const CFileDescriptorBase* pElements, INT_PTR nCount) +{ + ASSERT(nCount == 0 || + AfxIsValidAddress(pElements, (size_t)nCount * sizeof(CFileDescriptorBase), FALSE)); + + for(INT_PTR i = 0; i < nCount; i++) { + dc << "Element " << i << ":\n"; + pElements[i].Dump(dc); + } +} + +void CFileList1::Dump(CDumpContext& dc) const +{ + CObject::Dump(dc); + dc.SetDepth(1); + m_FileTree.Dump(dc); +} +#endif + + +// CDatFile1 +IMPLEMENT_DYNAMIC(CDatFile1, CObject) + +CDatFile1::CDatFile1() +{ +} + +CDatFile1::~CDatFile1() +{ + Close(); +} + +BOOL CDatFile1::Open(LPCTSTR lpszFileName, UINT nOpenFlags) +{ + if (!m_Dat.Open(lpszFileName, nOpenFlags)) { + return FALSE; + } + + if (m_Dat.GetLength() == 0) { + return TRUE; + } + + CArchive arFileList(&m_Dat, CArchive::load); + + TRY { + m_FileList.Serialize(arFileList); + } + + CATCH(CUserException, e) { + return FALSE; + } + + END_CATCH + + return TRUE; +} + +void CDatFile1::Close() +{ + Flush(); + + if (m_Dat.m_hFile != CFile::hFileNull) { + m_Dat.Close(); + } +} + +void CDatFile1::Flush() +{ + if (m_bNeedFlush) { + FreeSpaceForCatalog(); + m_Dat.SeekToBegin(); + CArchive arFileList(&m_Dat, CArchive::store); + m_FileList.Serialize(arFileList); + arFileList.Flush(); + m_bNeedFlush = FALSE; + } +} + +INT_PTR CDatFile1::GetFileCount() +{ + return m_FileList.GetSize(); +} + +CFileDescriptorBase CDatFile1::operator [](INT_PTR nIndex) const +{ + return m_FileList[nIndex]; +} + +INT_PTR CDatFile1::FindNext() +{ + for(++m_nCurrent; m_nCurrent < m_FileList.GetSize(); m_nCurrent++) { + if (MatchPattern(m_FileList[m_nCurrent].m_strFileName, m_strPattern, FALSE)) { + break; + } + } + + if (m_nCurrent == m_FileList.GetSize()) { + m_nCurrent = -1; + } + + return m_nCurrent; +} + +BOOL CDatFile1::Inflate(CArchive& ar) +{ + if (m_nCurrent == -1) { + return FALSE; + } + + CFileDescriptorBase descriptor = m_FileList[m_nCurrent]; + + m_Dat.Seek(descriptor.m_dwOffset, CFile::begin); + + const DWORD c_dwBufferSize = 65536; + BYTE buffer[c_dwBufferSize]; + + DWORD dwBytesLeft = descriptor.m_dwPackedFileLength; + DWORD dwBytesToRead; + DWORD dwRead; + DWORD dwBytesOut = 0; + + // Uncompressed file + if (descriptor.m_nType == 0) { + do { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + dwRead = m_Dat.Read(buffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + return FALSE; + } + + ar.Write(buffer, dwBytesToRead); + dwBytesOut += dwRead; + } while((dwBytesLeft -= dwRead ) > 0); + + return TRUE; + } + + BOOL bResult = TRUE; + BOOL bEndLoop = FALSE; + + do { + WORD wBlockDescriptor; + + if (ReadMSBWord(m_Dat, wBlockDescriptor) != sizeof(WORD)) { + bResult = FALSE; + break; + } + + dwBytesToRead = wBlockDescriptor & 0x7FFF; + + dwRead = m_Dat.Read(buffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + bResult = FALSE; + break; + } + + if (wBlockDescriptor & 0x8000) { + // Uncompressed block + ar.Write(buffer, dwRead); + } + else { + // Compressed block + LZSSDecodeBuffer(buffer, dwRead, ar); + } + } while((dwBytesLeft -= dwRead + sizeof(WORD)) > 0); + + return bResult; +} + +BOOL CDatFile1::Deflate(CArchive& ar, DWORD dwLength, LPCTSTR lpszName, int level) +{ + ULONGLONG ullDatLength = m_Dat.SeekToEnd(); + + // File descriptor + CFileDescriptorBase descriptor; + descriptor.m_dwFileLength = dwLength; + descriptor.m_strFileName = lpszName; + descriptor.m_dwOffset = DWORD(ullDatLength); + + // Buffers + const DWORD c_dwBufferSize = 65536; + BYTE buffer[c_dwBufferSize]; +// BYTE outBuffer[c_dwBufferSize]; + + DWORD dwBytesLeft = dwLength; + DWORD dwBytesToRead; + DWORD dwRead; + DWORD dwBytesOut = 0; + + // Flags + BOOL bResult = TRUE; + BOOL bUseInputArchive = TRUE; + + // Temporary file + char lpszTempFileName[MAX_PATH]; + CFile fileTemp; + BOOL bDeleteTempFile = FALSE; + + // Try compress file + if (level != 0) { + // Save position in input archive + ar.Flush(); + ULONGLONG ullArPosition = ar.GetFile()->GetPosition(); + + // Create temporary file + char lpszTempPath[MAX_PATH - 14]; + HANDLE hTempFile; + + GetTempPath(MAX_PATH - 14, lpszTempPath); + GetTempFileName(lpszTempPath, "DAT", 0, lpszTempFileName); + + hTempFile = ::CreateFile(lpszTempFileName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY, + NULL); + + if (hTempFile == INVALID_HANDLE_VALUE) { + return FALSE; + } + + fileTemp.m_hFile = hTempFile; + bDeleteTempFile = TRUE; + + // Deflate file + const DWORD c_dwBlockSize = 0x4000; + WORD wBlockDescriptor = 0; + ULONGLONG ullBlockDescPos; + + do { + // Read block of data + dwBytesToRead = (dwBytesLeft < c_dwBlockSize) ? dwBytesLeft : c_dwBlockSize; + + dwRead = ar.Read(buffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + bResult = FALSE; + break; + } + + // Write descriptor placeholder + ullBlockDescPos = fileTemp.GetPosition(); + WriteMSBWord(fileTemp, wBlockDescriptor); + + // Compress block + LZSSEncodeBuffer(buffer, dwRead, fileTemp); + + // Check length of compressed block + DWORD dwBlockLength = DWORD(fileTemp.GetPosition()) - DWORD(ullBlockDescPos) - sizeof(WORD); + + // More than uncompressed + if (dwBlockLength > dwRead) { + fileTemp.SetLength(ullBlockDescPos + sizeof(WORD)); + fileTemp.SeekToEnd(); + fileTemp.Write(buffer, dwRead); + wBlockDescriptor = WORD(dwRead) | 0x8000; + } + else { + wBlockDescriptor = WORD(dwBlockLength); + } + + // Patch descriptor + fileTemp.Seek(ullBlockDescPos, CFile::begin); + WriteMSBWord(fileTemp, wBlockDescriptor); + fileTemp.SeekToEnd(); + } while((dwBytesLeft -= dwRead) > 0); + + fileTemp.SeekToBegin(); + + // Check compression result + if (!bResult) { + fileTemp.Close(); + CFile::Remove(lpszTempFileName); + return FALSE; + } + + // Check length + ULONGLONG ullTempFileLength = fileTemp.GetLength(); + + if (ullTempFileLength < dwLength) { + bUseInputArchive = FALSE; + descriptor.m_dwPackedFileLength = DWORD(ullTempFileLength); + descriptor.m_nType = 1; + dwBytesLeft = DWORD(ullTempFileLength); + } + else { + ar.Flush(); + ar.GetFile()->Seek(ullArPosition, CFile::begin); + descriptor.m_dwPackedFileLength = dwLength; + descriptor.m_nType = 0; + dwBytesLeft = dwLength; + } + } + else { + descriptor.m_dwPackedFileLength = dwLength; + descriptor.m_nType = 0; + } + + // Set source archive + CArchive arTemp(&fileTemp, CArchive::load); // Temporary archive + CArchive* pArSource; + + if (bUseInputArchive) { + pArSource = &ar; + } + else { + pArSource = &arTemp; + } + + // Write data + TRY { + m_Dat.SeekToEnd(); + + do { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + dwRead = pArSource->Read(buffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + bResult = FALSE; + break; + } + + m_Dat.Write(buffer, dwBytesToRead); + dwBytesOut += dwRead; + } while((dwBytesLeft -= dwRead ) > 0); + + if (bResult) { + // Add descriptor + m_FileList.Add(descriptor); + m_bNeedFlush = TRUE; + } + else { + // Restore dat-file length on error + m_Dat.SetLength(ullDatLength); + } + + // Destroy temporary file + if (bDeleteTempFile) { + arTemp.Abort(); + fileTemp.Close(); + CFile::Remove(lpszTempFileName); + } + + return bResult; + } + + CATCH_ALL(e) { + m_Dat.SetLength(ullDatLength); + THROW_LAST(); + } + + END_CATCH_ALL +} + +BOOL CDatFile1::Delete() +{ + if (m_nCurrent == -1) { + return FALSE; + } + + m_FileList.RemoveAt(m_nCurrent); + m_nCurrent = -1; + m_bNeedFlush = TRUE; + + return TRUE; +} + + +CDatFile1* g_pDatFile1ForSort; + +int CompareDescriptorIndex1(const void* pIndex1, const void* pIndex2) +{ + INT_PTR nIndex1 = *((INT_PTR*)pIndex1); + INT_PTR nIndex2 = *((INT_PTR*)pIndex2); + + CFileDescriptorBase descriptor1; + CFileDescriptorBase descriptor2; + + descriptor1 = (*g_pDatFile1ForSort)[nIndex1]; + descriptor2 = (*g_pDatFile1ForSort)[nIndex2]; + + if (descriptor1.m_dwOffset < descriptor2.m_dwOffset) { + return -1; + } + else if (descriptor1.m_dwOffset > descriptor2.m_dwOffset) { + return 1; + } + else { + return 0; + } +} + +BOOL CDatFile1::Shrink(void (*pCallback)(INT_PTR nIndex)) +{ + if (m_Dat.GetLength() == 0) { + return FALSE; + } + + // Sort files by offset + INT_PTR nFileCount = GetFileCount(); + INT_PTR* pIndexArray = new INT_PTR[nFileCount]; + + if (pIndexArray == NULL) { + return FALSE; + } + + for(INT_PTR i = 0; i < nFileCount; i++) { + pIndexArray[i] = i; + } + + g_pDatFile1ForSort = this; + qsort(pIndexArray, nFileCount, sizeof(INT_PTR), CompareDescriptorIndex1); + + // Move files + const DWORD c_dwBufferSize = 65536; + BYTE buffer[c_dwBufferSize]; + + DWORD dwWritePos = m_FileList.GetLength(); + DWORD dwShift; + DWORD dwBytesToRead; + DWORD dwBytesLeft; + DWORD dwReadPos; + DWORD dwRead; + + CFileDescriptorBase descriptor; + + for(INT_PTR i = 0; i < nFileCount; i++) { + descriptor = (*this)[pIndexArray[i]]; + dwShift = descriptor.m_dwOffset - dwWritePos; + + if (dwShift > 0) { + m_bNeedFlush = TRUE; + + if (pCallback) { + pCallback(pIndexArray[i]); + } + + dwBytesLeft = descriptor.m_dwPackedFileLength; + dwReadPos = descriptor.m_dwOffset; + + descriptor.m_dwOffset = dwWritePos; + + do { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + + m_Dat.Seek(dwReadPos, CFile::begin); + dwRead = m_Dat.Read(buffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + delete [] pIndexArray; + return FALSE; + } + + m_Dat.Seek(dwWritePos, CFile::begin); + m_Dat.Write(buffer, dwRead); + dwWritePos += dwRead; + dwReadPos += dwRead; + } while((dwBytesLeft -= dwRead ) > 0); + + // Patch offsets + m_FileList[pIndexArray[i]].m_dwOffset = descriptor.m_dwOffset; + } + else { + dwWritePos += descriptor.m_dwPackedFileLength; + } + } + + m_Dat.SetLength(dwWritePos); + delete [] pIndexArray; + + Flush(); + return TRUE; +} + +void CDatFile1::FreeSpaceForCatalog() +{ + if (m_FileList.GetSize() != 0) { + DWORD dwCatalogSize = m_FileList.GetLength(); + + // Find offset + CFileDescriptorBase descriptor; + DWORD dwMinimalOffset = m_FileList[0].m_dwOffset; + + for(INT_PTR i = 1; i < m_FileList.GetSize(); i++) { + descriptor = m_FileList[i]; + + if (dwMinimalOffset > descriptor.m_dwOffset) { + dwMinimalOffset = descriptor.m_dwOffset; + } + } + + // Free space + if (dwMinimalOffset < dwCatalogSize) { + printf("Freeing space for catalog...\n"); + DWORD dwShift = dwCatalogSize - dwMinimalOffset; + DWORD dwBytesLeft = DWORD(m_Dat.GetLength()) - dwMinimalOffset; + DWORD dwTotalBytesToMove = dwBytesLeft; + DWORD dwReadPos = DWORD(m_Dat.GetLength()); + DWORD dwWritePos; + + const DWORD c_dwBufferSize = 65536; + BYTE buffer[c_dwBufferSize]; + DWORD dwBytesToRead; + DWORD dwRead; + + do { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + dwReadPos -= dwBytesToRead; + dwWritePos = dwReadPos + dwShift; + + m_Dat.Seek(dwReadPos, CFile::begin); + dwRead = m_Dat.Read(buffer, dwBytesToRead); + + m_Dat.Seek(dwWritePos, CFile::begin); + m_Dat.Write(buffer, dwRead); + + dwBytesLeft -= dwRead; + printf("Moved: %d of %d byte(s)\r", dwTotalBytesToMove - dwBytesLeft, dwTotalBytesToMove); + } while(dwBytesLeft > 0); + + // Patch offsets + for(INT_PTR i = 0; i < m_FileList.GetSize(); i++) { + m_FileList[i].m_dwOffset += dwShift; + } + } + + printf("\nSpace are freed.\n"); + } +} diff --git a/src/DatFile1.h b/src/DatFile1.h new file mode 100644 index 0000000..325880b --- /dev/null +++ b/src/DatFile1.h @@ -0,0 +1,91 @@ +#pragma once + +#include "DatFileBase.h" + +// CFileList1 +class CFileList1 : public CObject { +public: + CFileList1(); + virtual ~CFileList1(); + + DECLARE_DYNAMIC(CFileList1) + + virtual void Serialize(CArchive& ar); + INT_PTR GetSize() const; + + DWORD GetLength(); + + INT_PTR Add(const CFileDescriptorBase& newElement); + void RemoveAt(INT_PTR nIndex); + + CFileDescriptorBase& operator [] (INT_PTR nIndex); + CFileDescriptorBase operator [] (INT_PTR nIndex) const; + +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +private: + // Assignment and copy not allowed + CFileList1(const CFileList1&); + CFileList1& operator = (const CFileList1&); + +private: + // CFileDescriptorBaseArray + typedef CArray CFileDescriptorBaseArray; + + // CFileTreeItem + class CFileTreeItem { + public: + CFileTreeItem(); + CFileTreeItem(const CFileTreeItem& item); + + public: + CFileTreeItem& operator = (const CFileTreeItem& item); + + public: + CString m_strDirectoryName; + CFileDescriptorBaseArray m_FileList; + }; + + // CFileTree + typedef CArray CFileTree; + + CFileTree m_FileTree; +}; + +// CDatFile1 +class CDatFile1 : public CDatFileBase { +public: + CDatFile1(); + virtual ~CDatFile1(); + + DECLARE_DYNAMIC(CDatFile1) + + virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags); + virtual void Close(); + virtual void Flush(); + + virtual INT_PTR GetFileCount(); + virtual CFileDescriptorBase operator [] (INT_PTR nIndex) const; + + virtual INT_PTR FindNext(); + + virtual BOOL Inflate(CArchive& ar); + virtual BOOL Deflate(CArchive& ar, DWORD dwLength, LPCTSTR lpszName, int level); + virtual BOOL Delete(); + virtual BOOL Shrink(void (*pCallback)(INT_PTR nIndex)); + +private: + void FreeSpaceForCatalog(); + +private: + // Restricted operations + CDatFile1(const CDatFile1&); + CDatFile1& operator = (const CDatFile1&); + virtual void Serialize(CArchive&) {}; + +private: + CFileList1 m_FileList; +}; diff --git a/src/DatFile2.cpp b/src/DatFile2.cpp new file mode 100644 index 0000000..2538107 --- /dev/null +++ b/src/DatFile2.cpp @@ -0,0 +1,868 @@ +// DatFile2.cpp : implementation file +// + +#include "stdafx.h" +#include "DatFile2.h" + +#include + +#if defined(_UNICODE) || defined(UNICODE) +#error UNICODE not supported +#endif + + +// CFileDescriptor2 +IMPLEMENT_DYNAMIC(CFileDescriptor2, CFileDescriptorBase) + +CFileDescriptor2::CFileDescriptor2() +{ +} + +CFileDescriptor2::CFileDescriptor2(const CFileDescriptor2& item) : + CFileDescriptorBase(item) +{ +} + +CFileDescriptor2::~CFileDescriptor2() +{ +} + +CFileDescriptor2& CFileDescriptor2::operator = (const CFileDescriptor2& item) +{ + CFileDescriptorBase::operator = (item); + return (*this); +} + +void CFileDescriptor2::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) { + ASSERT(!m_strFileName.IsEmpty()); + + DWORD dwFileNameLength = m_strFileName.GetLength(); + + ar.Write(&dwFileNameLength, sizeof(dwFileNameLength)); + ar.WriteString(m_strFileName); + ar.Write(&m_nType, sizeof(m_nType)); + ar.Write(&m_dwFileLength, sizeof(m_dwFileLength)); + ar.Write(&m_dwPackedFileLength, sizeof(m_dwPackedFileLength)); + ar.Write(&m_dwOffset, sizeof(m_dwOffset)); + } + else { + DWORD dwFileNameLength; + LPTSTR lpszFileName; + + ar.Read(&dwFileNameLength, sizeof(dwFileNameLength)); + lpszFileName = m_strFileName.GetBufferSetLength(dwFileNameLength); + ar.Read(lpszFileName, dwFileNameLength); + m_strFileName.ReleaseBufferSetLength(dwFileNameLength); + + ar.Read(&m_nType, sizeof(m_nType)); + ar.Read(&m_dwFileLength, sizeof(m_dwFileLength)); + ar.Read(&m_dwPackedFileLength, sizeof(m_dwPackedFileLength)); + ar.Read(&m_dwOffset, sizeof(m_dwOffset)); + } +} + +int CFileDescriptor2::GetSize() const +{ + return sizeof(DWORD) + + m_strFileName.GetLength() + + sizeof(m_nType) + + sizeof(m_dwFileLength) + + sizeof(m_dwPackedFileLength) + + sizeof(m_dwOffset); +} + +#ifdef _DEBUG +void CFileDescriptor2::AssertValid() const +{ + CObject::AssertValid(); + ASSERT(!m_strFileName.IsEmpty()); + ASSERT(m_nType <= 1); + ASSERT(m_dwFileLength != 0); + ASSERT(m_dwPackedFileLength != 0); + ASSERT(m_dwPackedFileLength >= m_dwFileLength); +} + +void CFileDescriptor2::Dump(CDumpContext& dc) const +{ + CFileDescriptorBase::Dump(dc); + dc << "Size of descriptor:\t" << GetSize() << " bytes\n"; +} +#endif + + +// CFileList2 +IMPLEMENT_DYNAMIC(CFileList2, CObject) + +CFileList2::CFileList2() +{ +} + +CFileList2::~CFileList2() +{ +} + +void CFileList2::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) { + DWORD dwFileCount = DWORD(m_FileList.GetSize()); + ar.Write(&dwFileCount, sizeof(dwFileCount)); + + CFileDescriptor2 descriptor; + DWORD dwListLength = 0; + + for(DWORD i = 0; i < dwFileCount; i++) { + descriptor = m_FileList[i]; + descriptor.Serialize(ar); + dwListLength += descriptor.GetSize(); + } + + dwListLength += sizeof(DWORD); + ar.Write(&dwListLength, sizeof(dwListLength)); + } + else { + DWORD dwFileCount; + ar.Read(&dwFileCount, sizeof(dwFileCount)); + m_FileList.SetSize(dwFileCount); + + CFileDescriptor2 descriptor; + DWORD dwListLength; + + for(DWORD i = 0; i < dwFileCount; i++) { + descriptor.Serialize(ar); + m_FileList[i] = descriptor; + } + + ar.Read(&dwListLength, sizeof(dwListLength)); + } +} + +INT_PTR CFileList2::GetSize() const +{ + return m_FileList.GetSize(); +} + +INT_PTR CFileList2::Add(const CFileDescriptor2& newElement) +{ + // No elements + if (m_FileList.GetSize() == 0) { + return m_FileList.Add(newElement); + } + + // Less than first + if (MakeLower(newElement.m_strFileName) < + MakeLower(m_FileList[0].m_strFileName)) { + m_FileList.InsertAt(0, newElement); + return 0; + } + + // Greater than last + if (MakeLower(newElement.m_strFileName) > + MakeLower(m_FileList[m_FileList.GetUpperBound()].m_strFileName)) { + m_FileList.Add(newElement); + return m_FileList.GetUpperBound(); + } + + // Equally first + if (MakeLower(newElement.m_strFileName) == + MakeLower(m_FileList[0].m_strFileName)) { + m_FileList[0] = newElement; + return 0; + } + + // Equally last + if (MakeLower(newElement.m_strFileName) == + MakeLower(m_FileList[m_FileList.GetUpperBound()].m_strFileName)) { + m_FileList[m_FileList.GetUpperBound()] = newElement; + return m_FileList.GetUpperBound(); + } + + // Other + INT_PTR nLeft = 0; + INT_PTR nRight = m_FileList.GetUpperBound(); + INT_PTR nCurrent; + + while(nRight - nLeft > 1) { + nCurrent = (nRight + nLeft) >> 1; // divide by 2 + + if (MakeLower(newElement.m_strFileName) == MakeLower(m_FileList[nCurrent].m_strFileName)) { + m_FileList[nCurrent] = newElement; + return nCurrent; + } + else if (MakeLower(newElement.m_strFileName) < MakeLower(m_FileList[nCurrent].m_strFileName)) { + nRight = nCurrent; + } + else { + nLeft = nCurrent; + } + } + + m_FileList.InsertAt(nLeft + 1, newElement); + return (nLeft + 1); +} + +void CFileList2::RemoveAt(INT_PTR nIndex) +{ + m_FileList.RemoveAt(nIndex); +} + +CFileDescriptor2& CFileList2::operator [] (INT_PTR nIndex) +{ + return m_FileList[nIndex]; +} + +CFileDescriptor2 CFileList2::operator [] (INT_PTR nIndex) const +{ + return m_FileList[nIndex]; +} + +#ifdef _DEBUG +void CFileList2::AssertValid() const +{ + CObject::AssertValid(); + m_FileList.AssertValid(); + + for(INT_PTR i = 0; i < m_FileList.GetSize(); i++) { + m_FileList[i].AssertValid(); + } +} + +template<> +void AFXAPI DumpElements(CDumpContext& dc, const CFileDescriptor2* pElements, INT_PTR nCount) +{ + ASSERT(nCount == 0 || + AfxIsValidAddress(pElements, (size_t)nCount * sizeof(CFileDescriptor2), FALSE)); + + for(INT_PTR i = 0; i < nCount; i++) { + dc << "Element " << i << ":\n"; + pElements[i].Dump(dc); + } +} + +void CFileList2::Dump(CDumpContext& dc) const +{ + CObject::Dump(dc); + dc.SetDepth(1); + m_FileList.Dump(dc); +} +#endif + + +// CDatFile2 +IMPLEMENT_DYNAMIC(CDatFile2, CObject) + +CDatFile2::CDatFile2() +{ +} + +CDatFile2::~CDatFile2() +{ + Close(); +} + +BOOL CDatFile2::Open(LPCTSTR lpszFileName, UINT nOpenFlags) +{ + if (!m_Dat.Open(lpszFileName, nOpenFlags)) { + return FALSE; + } + + if (m_Dat.GetLength() == 0) { + return TRUE; + } + + DWORD dwDatLength; + DWORD dwFileListLength; + + ULONGLONG ullFileLength = m_Dat.GetLength(); + + if (ullFileLength < 12) { + m_Dat.Close(); + return FALSE; + } + + m_Dat.Seek(-8, CFile::end); + m_Dat.Read(&dwFileListLength, sizeof(dwFileListLength)); + m_Dat.Read(&dwDatLength, sizeof(dwDatLength)); + + if ((dwDatLength < 12) || (dwDatLength != ullFileLength)) { + m_Dat.Close(); + return FALSE; + } + + m_Dat.Seek(dwDatLength - dwFileListLength - 8, CFile::begin); + + CArchive arFileList(&m_Dat, CArchive::load); + + m_FileList.Serialize(arFileList); + return TRUE; +} + +void CDatFile2::Close() +{ + Flush(); + + if (m_Dat.m_hFile != CFile::hFileNull) { + m_Dat.Close(); + } +} + +void CDatFile2::Flush() +{ + if (m_bNeedFlush) { + ULONGLONG ullLength; + + TRY { + ullLength = m_Dat.SeekToEnd(); + CArchive arFileList(&m_Dat, CArchive::store); + m_FileList.Serialize(arFileList); + arFileList.Flush(); + DWORD dwTotalLength = DWORD(m_Dat.GetLength()) + 4; + m_Dat.Write(&dwTotalLength, sizeof(dwTotalLength)); + m_bNeedFlush = FALSE; + } + + CATCH_ALL(e) { + m_Dat.SetLength(ullLength); + THROW_LAST(); + } + + END_CATCH_ALL + } +} + +INT_PTR CDatFile2::GetFileCount() +{ + return m_FileList.GetSize(); +} + +CFileDescriptorBase CDatFile2::operator [](INT_PTR nIndex) const +{ + return m_FileList[nIndex]; +} + +INT_PTR CDatFile2::FindNext() +{ + for(++m_nCurrent; m_nCurrent < m_FileList.GetSize(); m_nCurrent++) { + if (MatchPattern(m_FileList[m_nCurrent].m_strFileName, m_strPattern, FALSE)) { + break; + } + } + + if (m_nCurrent == m_FileList.GetSize()) { + m_nCurrent = -1; + } + + return m_nCurrent; +} + +BOOL CDatFile2::Inflate(CArchive& ar) +{ + if (m_nCurrent == -1) { + return FALSE; + } + + CFileDescriptorBase descriptor = m_FileList[m_nCurrent]; + + m_Dat.Seek(descriptor.m_dwOffset, CFile::begin); + + const DWORD c_dwBufferSize = 65536; + BYTE inBuffer[c_dwBufferSize]; + BYTE outBuffer[c_dwBufferSize]; + + DWORD dwBytesLeft = descriptor.m_dwPackedFileLength; + DWORD dwBytesToRead; + DWORD dwRead; + DWORD dwBytesOut = 0; + + // Uncompressed file + if (descriptor.m_nType == 0) { + do { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + dwRead = m_Dat.Read(inBuffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + return FALSE; + } + + ar.Write(inBuffer, dwBytesToRead); + dwBytesOut += dwRead; + } while((dwBytesLeft -= dwRead ) > 0); + + return TRUE; + } + + int rc; + z_stream stream; + + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = Z_NULL; + stream.avail_in = 0; + + rc = ::inflateInit(&stream); + + if (rc != Z_OK) { + return FALSE; + } + + stream.next_out = outBuffer; + stream.avail_out = c_dwBufferSize; + + BOOL bNeedData = TRUE; + BOOL bResult = TRUE; + BOOL bEndLoop = FALSE; + BOOL bWriteOutData = FALSE; + + do { + if (bNeedData) { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + dwRead = m_Dat.Read(inBuffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + bResult = FALSE; + bWriteOutData = FALSE; + break; + } + else { + dwBytesLeft -= dwRead; + } + + stream.next_in = inBuffer; + stream.avail_in = dwRead; + bNeedData = FALSE; + } + + rc = ::inflate(&stream, Z_SYNC_FLUSH); + + switch(rc) { + case Z_OK: { + if (stream.avail_in == 0) { + if (dwBytesLeft != 0) { + // Need more input + bNeedData = TRUE; + } + else { + // All data inflated, but Z_STREAM_END not returned + bEndLoop = TRUE; + bWriteOutData = TRUE; + break; + } + } + + if (stream.avail_out == 0) { // Need more output + bWriteOutData = TRUE; + } + + break; + } + + case Z_STREAM_END: { // Inflated + bEndLoop = TRUE; + bWriteOutData = TRUE; + break; + } + + default: { // Error + bEndLoop = TRUE; + bResult = FALSE; + } + } + + if (bWriteOutData) { + ar.Write(outBuffer, c_dwBufferSize - stream.avail_out); + bWriteOutData = FALSE; + stream.next_out = outBuffer; + stream.avail_out = c_dwBufferSize; + } + } while(!bEndLoop); + + rc = ::inflateEnd(&stream); + return bResult; +} + +BOOL CDatFile2::Deflate(CArchive& ar, DWORD dwLength, LPCTSTR lpszName, int level) +{ + // Cut tail of file with files list + if (m_Dat.GetLength() != 0) { + if (!m_bNeedFlush) { + DWORD dwDatLength; + DWORD dwFileListLength; + + m_Dat.Seek(-8, CFile::end); + m_Dat.Read(&dwFileListLength, sizeof(dwFileListLength)); + m_Dat.Read(&dwDatLength, sizeof(dwDatLength)); + m_Dat.SetLength(dwDatLength - dwFileListLength - 8); + } + } + + ULONGLONG ullDatLength = m_Dat.SeekToEnd(); + + // File descriptor + CFileDescriptor2 descriptor; + descriptor.m_dwFileLength = dwLength; + descriptor.m_strFileName = lpszName; + descriptor.m_dwOffset = DWORD(ullDatLength); + + // Buffers + const DWORD c_dwBufferSize = 65536; + BYTE inBuffer[c_dwBufferSize]; + BYTE outBuffer[c_dwBufferSize]; + + DWORD dwBytesLeft = dwLength; + DWORD dwBytesToRead; + DWORD dwRead; + DWORD dwBytesOut = 0; + + // Flags + BOOL bResult = TRUE; + BOOL bUseInputArchive = TRUE; + + // Temporary file + char lpszTempFileName[MAX_PATH]; + CFile fileTemp; + BOOL bDeleteTempFile = FALSE; + + // Try compress file + if (level != 0) { + // Save position in input archive + ar.Flush(); + ULONGLONG ullArPosition = ar.GetFile()->GetPosition(); + + // Create temporary file + char lpszTempPath[MAX_PATH - 14]; + HANDLE hTempFile; + + GetTempPath(MAX_PATH - 14, lpszTempPath); + GetTempFileName(lpszTempPath, "DAT", 0, lpszTempFileName); + + hTempFile = ::CreateFile(lpszTempFileName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY, + NULL); + + if (hTempFile == INVALID_HANDLE_VALUE) { + return FALSE; + } + + fileTemp.m_hFile = hTempFile; + bDeleteTempFile = TRUE; + + // Deflate file + if (level < 0 || level > 9) { + level = 9; + } + + int rc; + z_stream stream; + int flush; + + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = Z_NULL; + stream.avail_in = 0; + + rc = ::deflateInit(&stream, level); + + if (rc != Z_OK) { + return FALSE; + } + + stream.next_out = outBuffer; + stream.avail_out = c_dwBufferSize; + + BOOL bNeedData = TRUE; + BOOL bEndLoop = FALSE; + BOOL bWriteOutData = FALSE; + + do { + if (bNeedData) { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + dwRead = ar.Read(inBuffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + bResult = FALSE; + bWriteOutData = FALSE; + break; + } + else { + dwBytesLeft -= dwRead; + flush = (dwBytesLeft == 0) ? Z_FINISH : Z_SYNC_FLUSH; + } + + stream.next_in = inBuffer; + stream.avail_in = dwRead; + + bNeedData = FALSE; + } + + rc = ::deflate(&stream, flush); + + switch(rc) { + case Z_OK: { + if (stream.avail_in == 0) { // Need more input + bNeedData = TRUE; + } + + if (stream.avail_out == 0) { // Need more output + bWriteOutData = TRUE; + } + + break; + } + + case Z_STREAM_END: { // Deflated + bEndLoop = TRUE; + bWriteOutData = TRUE; + break; + } + + default: { // Error + bEndLoop = TRUE; + bResult = FALSE; + } + } + + if (bWriteOutData) { + fileTemp.Write(outBuffer, c_dwBufferSize - stream.avail_out); + bWriteOutData = FALSE; + stream.next_out = outBuffer; + stream.avail_out = c_dwBufferSize; + } + } while(!bEndLoop); + + rc = ::deflateEnd(&stream); + fileTemp.SeekToBegin(); + + // Check compression result + if (!bResult) { + fileTemp.Close(); + CFile::Remove(lpszTempFileName); + return FALSE; + } + + // Check length + ULONGLONG ullTempFileLength = fileTemp.GetLength(); + + if (ullTempFileLength < dwLength) { + bUseInputArchive = FALSE; + descriptor.m_dwPackedFileLength = DWORD(ullTempFileLength); + descriptor.m_nType = 1; + dwBytesLeft = DWORD(ullTempFileLength); + } + else { + ar.Flush(); + ar.GetFile()->Seek(ullArPosition, CFile::begin); + descriptor.m_dwPackedFileLength = dwLength; + descriptor.m_nType = 0; + dwBytesLeft = dwLength; + } + } + else { + descriptor.m_dwPackedFileLength = dwLength; + descriptor.m_nType = 0; + } + + // Set source archive + CArchive arTemp(&fileTemp, CArchive::load); // Temporary archive + CArchive* pArSource; + + if (bUseInputArchive) { + pArSource = &ar; + } + else { + pArSource = &arTemp; + } + + // Write data + TRY { + do { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + dwRead = pArSource->Read(inBuffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + bResult = FALSE; + break; + } + + m_Dat.Write(inBuffer, dwBytesToRead); + dwBytesOut += dwRead; + } while((dwBytesLeft -= dwRead ) > 0); + + if (bResult) { + // Add descriptor + m_FileList.Add(descriptor); + m_bNeedFlush = TRUE; + } + else { + // Restore dat-file length on error + m_Dat.SetLength(ullDatLength); + } + + // Destroy temporary file + if (bDeleteTempFile) { + arTemp.Abort(); + fileTemp.Close(); + CFile::Remove(lpszTempFileName); + } + + return bResult; + } + + CATCH_ALL(e) { + m_Dat.SetLength(ullDatLength); + THROW_LAST(); + } + + END_CATCH_ALL +} + +BOOL CDatFile2::Delete() +{ + if (m_nCurrent == -1) { + return FALSE; + } + + // Cut tail of file with files list + if (m_Dat.GetLength() != 0) { + if (!m_bNeedFlush) { + DWORD dwDatLength; + DWORD dwFileListLength; + + m_Dat.Seek(-8, CFile::end); + m_Dat.Read(&dwFileListLength, sizeof(dwFileListLength)); + m_Dat.Read(&dwDatLength, sizeof(dwDatLength)); + m_Dat.SetLength(dwDatLength - dwFileListLength - 8); + } + } + + m_FileList.RemoveAt(m_nCurrent); + m_nCurrent = -1; + m_bNeedFlush = TRUE; + + return TRUE; +} + + +CDatFile2* g_pDatFile2ForSort; + +int CompareDescriptorIndex2(const void* pIndex1, const void* pIndex2) +{ + INT_PTR nIndex1 = *((INT_PTR*)pIndex1); + INT_PTR nIndex2 = *((INT_PTR*)pIndex2); + + CFileDescriptorBase descriptor1; + CFileDescriptorBase descriptor2; + + descriptor1 = (*g_pDatFile2ForSort)[nIndex1]; + descriptor2 = (*g_pDatFile2ForSort)[nIndex2]; + + if (descriptor1.m_dwOffset < descriptor2.m_dwOffset) { + return -1; + } + else if (descriptor1.m_dwOffset > descriptor2.m_dwOffset) { + return 1; + } + else { + return 0; + } +} + +BOOL CDatFile2::Shrink(void (*pCallback)(INT_PTR nIndex)) +{ + if (m_Dat.GetLength() == 0) { + return FALSE; + } + + // Sort files by offset + INT_PTR nFileCount = GetFileCount(); + INT_PTR* pIndexArray = new INT_PTR[nFileCount]; + + if (pIndexArray == NULL) { + return FALSE; + } + + for(INT_PTR i = 0; i < nFileCount; i++) { + pIndexArray[i] = i; + } + + g_pDatFile2ForSort = this; + qsort(pIndexArray, nFileCount, sizeof(INT_PTR), CompareDescriptorIndex2); + + // Move files + const DWORD c_dwBufferSize = 65536; + BYTE buffer[c_dwBufferSize]; + + DWORD dwWritePos = 0; + DWORD dwShift; + DWORD dwBytesToRead; + DWORD dwBytesLeft; + DWORD dwReadPos; + DWORD dwRead; + + CFileDescriptorBase descriptor; + + for(INT_PTR i = 0; i < nFileCount; i++) { + descriptor = (*this)[pIndexArray[i]]; + dwShift = descriptor.m_dwOffset - dwWritePos; + + if (dwShift > 0) { + if (pCallback) { + pCallback(pIndexArray[i]); + } + + if (!m_bNeedFlush) { + DWORD dwDatLength; + DWORD dwFileListLength; + + m_Dat.Seek(-8, CFile::end); + m_Dat.Read(&dwFileListLength, sizeof(dwFileListLength)); + m_Dat.Read(&dwDatLength, sizeof(dwDatLength)); + m_Dat.SetLength(dwDatLength - dwFileListLength - 8); + m_bNeedFlush = TRUE; + } + + dwBytesLeft = descriptor.m_dwPackedFileLength; + dwReadPos = descriptor.m_dwOffset; + + descriptor.m_dwOffset = dwWritePos; + + do { + dwBytesToRead = (dwBytesLeft < c_dwBufferSize) ? dwBytesLeft : c_dwBufferSize; + + m_Dat.Seek(dwReadPos, CFile::begin); + dwRead = m_Dat.Read(buffer, dwBytesToRead); + + if (dwRead != dwBytesToRead) { + delete [] pIndexArray; + return FALSE; + } + + m_Dat.Seek(dwWritePos, CFile::begin); + m_Dat.Write(buffer, dwRead); + dwWritePos += dwRead; + dwReadPos += dwRead; + } while((dwBytesLeft -= dwRead ) > 0); + + // Patch offsets + m_FileList[pIndexArray[i]].m_dwOffset = descriptor.m_dwOffset; + } + else { + dwWritePos += descriptor.m_dwPackedFileLength; + } + } + + m_Dat.SetLength(dwWritePos); + delete [] pIndexArray; + + m_bNeedFlush = TRUE; + + Flush(); + + return TRUE; +} diff --git a/src/DatFile2.h b/src/DatFile2.h new file mode 100644 index 0000000..ac5c146 --- /dev/null +++ b/src/DatFile2.h @@ -0,0 +1,90 @@ +#pragma once + +#include "DatFileBase.h" + +// CFileDescriptor2 +class CFileDescriptor2 : public CFileDescriptorBase { +public: + CFileDescriptor2(); + CFileDescriptor2(const CFileDescriptor2& item); + virtual ~CFileDescriptor2(); + + DECLARE_DYNAMIC(CFileDescriptor2) + + virtual void Serialize(CArchive& ar); + int GetSize() const; + + CFileDescriptor2& operator = (const CFileDescriptor2& item); + +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif +}; + + +// CFileList2 +class CFileList2 : public CObject { +public: + CFileList2(); + virtual ~CFileList2(); + + DECLARE_DYNAMIC(CFileList2) + + virtual void Serialize(CArchive& ar); + INT_PTR GetSize() const; + + INT_PTR Add(const CFileDescriptor2& newElement); + void RemoveAt(INT_PTR nIndex); + + CFileDescriptor2& operator [] (INT_PTR nIndex); + CFileDescriptor2 operator [] (INT_PTR nIndex) const; + +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +private: + // Assignment and copy not allowed + CFileList2(const CFileList2&); + CFileList2& operator = (const CFileList2&); + +private: + typedef CArray CFileDescriptor2Array; + + CFileDescriptor2Array m_FileList; +}; + +// CDatFile2 +class CDatFile2 : public CDatFileBase { +public: + CDatFile2(); + virtual ~CDatFile2(); + + DECLARE_DYNAMIC(CDatFile2) + + virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags); + virtual void Close(); + virtual void Flush(); + + virtual INT_PTR GetFileCount(); + virtual CFileDescriptorBase operator [](INT_PTR nIndex) const; + + virtual INT_PTR FindNext(); + + virtual BOOL Inflate(CArchive& ar); + virtual BOOL Deflate(CArchive& ar, DWORD dwLength, LPCTSTR lpszName, int level); + virtual BOOL Delete(); + + virtual BOOL Shrink(void (*pCallback)(INT_PTR nIndex)); + +private: + // Restricted operations + CDatFile2(const CDatFile2&); + CDatFile2& operator = (const CDatFile2&); + virtual void Serialize(CArchive&) {}; + +private: + CFileList2 m_FileList; +}; diff --git a/src/DatFileBase.cpp b/src/DatFileBase.cpp new file mode 100644 index 0000000..8f5acaf --- /dev/null +++ b/src/DatFileBase.cpp @@ -0,0 +1,163 @@ +// DatFile.cpp : implementation file +// + +#include "stdafx.h" +#include "DatFileBase.h" +#include ".\datfilebase.h" + +#if defined(_UNICODE) || defined(UNICODE) +#error UNICODE not supported +#endif + +// Utility function +CString MakeLower(CString str) +{ + return str.MakeLower(); +} + +// CFileDescriptorBase +IMPLEMENT_DYNAMIC(CFileDescriptorBase, CObject) + +CFileDescriptorBase::CFileDescriptorBase() : + m_strFileName(""), + m_nType(0), + m_dwFileLength(0), + m_dwPackedFileLength(0), + m_dwOffset(0) +{ +} + +CFileDescriptorBase::CFileDescriptorBase(const CFileDescriptorBase& item) : + m_strFileName(item.m_strFileName), + m_nType(item.m_nType), + m_dwFileLength(item.m_dwFileLength), + m_dwPackedFileLength(item.m_dwPackedFileLength), + m_dwOffset(item.m_dwOffset) +{ +} + +CFileDescriptorBase::~CFileDescriptorBase() +{ +} + +CFileDescriptorBase& CFileDescriptorBase::operator = (const CFileDescriptorBase& item) +{ + if (&item != this) { + m_strFileName = item.m_strFileName; + m_nType = item.m_nType; + m_dwFileLength = item.m_dwFileLength; + m_dwPackedFileLength = item.m_dwPackedFileLength; + m_dwOffset = item.m_dwOffset; + } + + return (*this); +} + +void CFileDescriptorBase::Serialize(CArchive& ar) +{ + ASSERT(FALSE); +} + +#ifdef _DEBUG +void CFileDescriptorBase::AssertValid() const +{ + CObject::AssertValid(); + ASSERT(!m_strFileName.IsEmpty()); + ASSERT(m_nType <= 1); + ASSERT(m_dwFileLength != 0); + ASSERT(m_dwPackedFileLength != 0); + ASSERT(m_dwPackedFileLength >= m_dwFileLength); +} + +void CFileDescriptorBase::Dump(CDumpContext& dc) const +{ + CObject::Dump(dc); + dc << "File name length:\t" << m_strFileName.GetLength() << "\n"; + dc << "File name:\t\t\t" << m_strFileName << "\n"; + dc << "Type:\t\t\t\t" << m_nType << "\n"; + dc << "File length:\t\t\t" << m_dwFileLength << " ("; + dc.DumpAsHex(m_dwFileLength); + dc << ")\n"; + dc << "Packed file length:\t" << m_dwPackedFileLength << " ("; + dc.DumpAsHex(m_dwPackedFileLength); + dc << ")\n"; + dc << "Offset:\t\t\t\t" << m_dwOffset << " ("; + dc.DumpAsHex(m_dwOffset); + dc << ")\n"; +} +#endif + +// CDatFileBase +IMPLEMENT_DYNAMIC(CDatFileBase, CObject) + +CDatFileBase::CDatFileBase() : + m_strPattern("*"), + m_bNeedFlush(FALSE), + m_nCurrent(-1) +{ +} + +CDatFileBase::~CDatFileBase() +{ +} + +DWORD CDatFileBase::GetLength(void) +{ + return DWORD(m_Dat.GetLength()); +} + +INT_PTR CDatFileBase::FindFirst(LPCTSTR lpszPattern) +{ + m_strPattern = lpszPattern; + m_nCurrent = -1; + return FindNext(); +} + +INT_PTR CDatFileBase::GetCurrentFileIndex() +{ + return m_nCurrent; +} + +CString CDatFileBase::GetFindFilePattern() +{ + return m_strPattern; +} + +BOOL CDatFileBase::MatchPattern(LPCTSTR lpszString, LPCTSTR lpszPattern, BOOL bCaseSensitive) +{ + char c, p; + + for(; ;) { + switch(p = ConvertCase(*lpszPattern++, bCaseSensitive)) { + case 0: // end of lpszPattern + return *lpszString ? FALSE : TRUE; // if end of lpszString TRUE + + case '*': + while (*lpszString) { // match zero or more char + if (MatchPattern(lpszString++, lpszPattern, bCaseSensitive)) + return TRUE; + } + + return MatchPattern (lpszString, lpszPattern, bCaseSensitive ); + + case '?': + if (*lpszString++ == 0) // match any one char + return FALSE; // not end of lpszString + + break; + + default: + c = ConvertCase(*lpszString++, bCaseSensitive); + + if (c != p) // check for exact char + return FALSE; // not a match + + break; + } + } +} + +char CDatFileBase::ConvertCase(char c, BOOL bCaseSensetive) +{ + return bCaseSensetive ? c : char(CharUpper(LPTSTR(c))); +} diff --git a/src/DatFileBase.h b/src/DatFileBase.h new file mode 100644 index 0000000..dcad5a1 --- /dev/null +++ b/src/DatFileBase.h @@ -0,0 +1,71 @@ +#pragma once + +// CFileDescriptorBase +class CFileDescriptorBase : public CObject { +public: + CFileDescriptorBase(); + CFileDescriptorBase(const CFileDescriptorBase& item); + virtual ~CFileDescriptorBase(); + + DECLARE_DYNAMIC(CFileDescriptorBase) + + virtual void Serialize(CArchive& ar); + + CFileDescriptorBase& operator = (const CFileDescriptorBase& item); + +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +public: + CString m_strFileName; + BYTE m_nType; + DWORD m_dwFileLength; + DWORD m_dwPackedFileLength; + DWORD m_dwOffset; +}; + + +// CDatFileBase +class CDatFileBase : public CObject { +public: + CDatFileBase(); + virtual ~CDatFileBase(); + + DECLARE_DYNAMIC(CDatFileBase) + + virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags) = 0; + virtual void Close() = 0; + virtual void Flush() = 0; + + DWORD GetLength(); + + virtual INT_PTR GetFileCount() = 0; + virtual CFileDescriptorBase operator [](INT_PTR nIndex) const = 0; + + INT_PTR FindFirst(LPCTSTR lpszPattern); + virtual INT_PTR FindNext() = 0; + INT_PTR GetCurrentFileIndex(); + CString GetFindFilePattern(); + + virtual BOOL Inflate(CArchive& ar) = 0; + virtual BOOL Deflate(CArchive& ar, DWORD dwLength, LPCTSTR lpszName, int level) = 0; + virtual BOOL Delete() = 0; + virtual BOOL Shrink(void (*pCallback)(INT_PTR nIndex)) = 0; + +protected: + BOOL MatchPattern(LPCTSTR lpszString, LPCTSTR lpszPattern, BOOL bCaseSensitive); + char ConvertCase(char c, BOOL bCaseSensetive); + +protected: + CString m_strPattern; + CFile m_Dat; + + BOOL m_bNeedFlush; + INT_PTR m_nCurrent; +}; + + +// Utility function +CString MakeLower(CString str); diff --git a/src/LZ77C.cpp b/src/LZ77C.cpp new file mode 100644 index 0000000..47be675 --- /dev/null +++ b/src/LZ77C.cpp @@ -0,0 +1,304 @@ +#include "stdafx.h" + +#define N 4096 /* size of ring buffer */ +#define F 18 /* upper limit for match_length */ +#define THRESHOLD 2 /* encode string into position and length if match_length is greater than this */ +#define NIL N /* index for root of binary search trees */ + +//unsigned long int textsize = 0; /* text size counter */ +//unsigned long int codesize = 0; /* code size counter */ + +BYTE text_buf[N + F - 1]; /* Ring buffer of size N, with extra F-1 bytes to facilitate string comparison */ + +int match_position; /* Longest match. These are set by the InsertNode() procedure. */ +int match_length; + +int lson[N + 1]; /* left children */ +int rson[N + 257]; /* right children */ +int dad[N + 1]; /* parents -- These constitute binary search trees. */ + +void InitTree() +{ + int i; + + for(i = N + 1; i <= N + 256; i++) { + rson[i] = NIL; + } + + for(i = 0; i < N; i++) { + dad[i] = NIL; + } +} + +void InsertNode(int r) +{ + int i; + int p; + int cmp; + BYTE* key; + + cmp = 1; + key = &text_buf[r]; + p = N + 1 + key[0]; + + rson[r] = lson[r] = NIL; + match_length = 0; + + for( ; ; ) { + if (cmp >= 0) { + if (rson[p] != NIL) { + p = rson[p]; + } + else { + rson[p] = r; + dad[r] = p; + return; + } + } + else { + if (lson[p] != NIL) { + p = lson[p]; + } + else { + lson[p] = r; + dad[r] = p; + return; + } + } + + for (i = 1; i < F; i++) { + if ((cmp = key[i] - text_buf[p + i]) != 0) { + break; + } + } + + if (i > match_length) { + match_position = p; + + if ((match_length = i) >= F) { + break; + } + } + } + + dad[r] = dad[p]; + lson[r] = lson[p]; + rson[r] = rson[p]; + + dad[lson[p]] = r; + dad[rson[p]] = r; + + if (rson[dad[p]] == p) { + rson[dad[p]] = r; + } + else { + lson[dad[p]] = r; + } + + dad[p] = NIL; /* remove p */ +} + +void DeleteNode(int p) +{ + int q; + + if (dad[p] == NIL) { + return; /* not in tree */ + } + + if (rson[p] == NIL) { + q = lson[p]; + } + else if (lson[p] == NIL) { + q = rson[p]; + } + else { + q = lson[p]; + + if (rson[q] != NIL) { + do { + q = rson[q]; + } while (rson[q] != NIL); + + rson[dad[q]] = lson[q]; + dad[lson[q]] = dad[q]; + lson[q] = lson[p]; + dad[lson[p]] = q; + } + + rson[q] = rson[p]; + dad[rson[p]] = q; + } + + dad[q] = dad[p]; + + if (rson[dad[p]] == p) { + rson[dad[p]] = q; + } + else { + lson[dad[p]] = q; + } + + dad[p] = NIL; +} + +#define GETBYTE() ((dwInputCurrent < dwInputLength) ? int(buffer[dwInputCurrent++]) & 0xFF : (-1)) + +void LZSSEncodeBuffer(BYTE* buffer, DWORD dwInputLength, CFile& fileOut) +{ + ASSERT(buffer); + + int i; + int c; + int len; + int r; + int s; + int last_match_length; + int code_buf_ptr; + + BYTE code_buf[17]; + BYTE mask; + + DWORD dwInputCurrent = 0; + + InitTree(); + + code_buf[0] = 0; + code_buf_ptr = mask = 1; + s = 0; + r = N - F; + + for(i = s; i < r; i++) { + text_buf[i] = ' '; + } + + for(len = 0; len < F && (c = GETBYTE()) != (-1); len++) { + text_buf[r + len] = c; + } + + if (len == 0) { + return; + } + + for(i = 1; i <= F; i++) { + InsertNode(r - i); + } + + InsertNode(r); + + do { + if (match_length > len) { + match_length = len; + } + + if (match_length <= THRESHOLD) { + match_length = 1; + code_buf[0] |= mask; + code_buf[code_buf_ptr++] = text_buf[r]; + } + else { + code_buf[code_buf_ptr++] = (BYTE) match_position; + code_buf[code_buf_ptr++] = (BYTE)(((match_position >> 4) & 0xf0) | (match_length - (THRESHOLD + 1))); + } + + if ((mask <<= 1) == 0) { + for (i = 0; i < code_buf_ptr; i++) { + fileOut.Write(code_buf + i, 1); + } + + code_buf[0] = 0; + code_buf_ptr = mask = 1; + } + + last_match_length = match_length; + + for (i = 0; i < last_match_length && (c = GETBYTE()) != (-1); i++) { + DeleteNode(s); + text_buf[s] = c; + + if (s < F - 1) { + text_buf[s + N] = c; + } + + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + InsertNode(r); + } + + while (i++ < last_match_length) { + DeleteNode(s); + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + + if (--len) { + InsertNode(r); + } + } + } while(len > 0); + + if (code_buf_ptr > 1) { + for (i = 0; i < code_buf_ptr; i++) { + fileOut.Write(code_buf + i, 1); + } + } +} + +void LZSSDecodeBuffer(BYTE* buffer, DWORD dwInputLength, CArchive& arOut) +{ + ASSERT(buffer); + + int i; + int j; + int k; + int r; + int c; + unsigned int flags; + + DWORD dwInputCurrent = 0; + + for (i = 0; i < N - F; i++) { + text_buf[i] = ' '; + } + + r = N - F; + flags = 0; + + for ( ; ; ) { + if (((flags >>= 1) & 256) == 0) { + if ((c = GETBYTE()) == (-1)) { + break; + } + + flags = c | 0xff00; /* uses higher byte cleverly */ + } /* to count eight */ + + if (flags & 1) { + if ((c = GETBYTE()) == (-1)) { + break; + } + + arOut.Write(&c, 1); + text_buf[r++] = c; + r &= (N - 1); + } + else { + if ((i = GETBYTE()) == (-1)) { + break; + } + + if ((j = GETBYTE()) == (-1)) { + break; + } + + i |= ((j & 0xf0) << 4); + j = (j & 0x0f) + THRESHOLD; + + for (k = 0; k <= j; k++) { + c = text_buf[(i + k) & (N - 1)]; + arOut.Write(&c, 1); + text_buf[r++] = c; + r &= (N - 1); + } + } + } +} diff --git a/src/LZ77C.h b/src/LZ77C.h new file mode 100644 index 0000000..e5d260f --- /dev/null +++ b/src/LZ77C.h @@ -0,0 +1,7 @@ +#ifndef LZ77C_H +#define LZ77C_H + +void LZSSEncodeBuffer(BYTE* buffer, DWORD dwInputLength, CFile& fileOut); +void LZSSDecodeBuffer(BYTE* buffer, DWORD dwInputLength, CArchive& arOut); + +#endif /* LZ77C_H */ diff --git a/src/OnAdd.cpp b/src/OnAdd.cpp new file mode 100644 index 0000000..e0f1e37 --- /dev/null +++ b/src/OnAdd.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" +#include "dat2.h" +#include "Util.h" + +extern CString g_strTargetDir; + +DWORD g_dwTotalAdded; + +BOOL ProcessTree(CString strPattern, CString strRoot, BOOL bRecursive) +{ + BOOL bResult = TRUE; + + CString strPath = ExtractFilePath(strPattern); + CString strFilePattern = ExtractFileName(strPattern); + + WIN32_FIND_DATA fd; + HANDLE hSearchHandle = FindFirstFile(strPattern, &fd); + + if (hSearchHandle != INVALID_HANDLE_VALUE) { + do { + if (CString(fd.cFileName) != CString(_T(".")) && + CString(fd.cFileName) != CString(_T(".."))) { + + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (bRecursive) { + ProcessTree(strPath + fd.cFileName + "\\" + strFilePattern, + strRoot.IsEmpty() ? fd.cFileName : strRoot + "\\" + fd.cFileName, // + _T("\\"), + bRecursive); + } + } + else { + CFile fileIn; + + if (fileIn.Open(strPath + fd.cFileName, CFile::modeRead | CFile::shareDenyWrite)) { + CArchive arIn(&fileIn, CArchive::load); + CString strFileDescriptorName = g_strTargetDir; + strFileDescriptorName += strRoot.IsEmpty() ? fd.cFileName : strRoot + _T("\\") + fd.cFileName; + + printf("Adding: %s", strFileDescriptorName); + + if (g_pDatFile->Deflate(arIn, DWORD(fileIn.GetLength()), + strFileDescriptorName, g_nCompressMethod)) { + printf("\n"); + g_dwTotalAdded++; + } + else { + printf(" Error!!!\n"); + bResult = FALSE; + break; + } + } + else { + bResult = FALSE; + break; + } + } + } + } while(FindNextFile(hSearchHandle, &fd)); + + FindClose(hSearchHandle); + return bResult; + } + else { + return FALSE; + } +} + +int OnAdd() +{ + g_dwTotalAdded = 0; + + if (g_pDatFile->Open(g_strDatFileName, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite | CFile::shareDenyWrite)) { + for(INT_PTR j = 0; j < g_FileList.GetCount(); j++) { + CString strRoot = ExtractFilePath(g_FileList[j], FALSE); + + if (strRoot[0] == '\\') { + strRoot = strRoot.Right(strRoot.GetLength() - 1); + } + + if (strRoot.Right(1) == '\\') { + strRoot = strRoot.Left(strRoot.GetLength() - 1); + } + + if (!ProcessTree(g_FileList[j], strRoot, g_bRecurseIntoDir)) { + return 4; + } + } + + printf("----------\n"); + printf("%u file(s) added\n", g_dwTotalAdded); + + return 0; + } + else { + printf("Error: Unable open file %s\n", LPCTSTR(g_strDatFileName)); + return 1; + } +} \ No newline at end of file diff --git a/src/OnDelete.cpp b/src/OnDelete.cpp new file mode 100644 index 0000000..5b4bb31 --- /dev/null +++ b/src/OnDelete.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "dat2.h" + +int OnDelete() +{ + CFileDescriptorBase descriptor; + DWORD dwTotalDeleted = 0; + + for(INT_PTR j = 0; j < g_FileList.GetCount(); j++) { + if (g_pDatFile->FindFirst(g_FileList[j]) != -1) { + do { + descriptor = (*g_pDatFile)[g_pDatFile->GetCurrentFileIndex()]; + printf("Deleting: %s", descriptor.m_strFileName); + + if (g_pDatFile->Delete()) { + dwTotalDeleted++; + printf("\n"); + } + else { + printf(" Error!!!\n"); + return 2; + } + } while(g_pDatFile->FindNext() != -1); + } + } + + printf("----------\n"); + printf("%u file(s) deleted\n", dwTotalDeleted); + + return 0; +} \ No newline at end of file diff --git a/src/OnExtract.cpp b/src/OnExtract.cpp new file mode 100644 index 0000000..dd5b23c --- /dev/null +++ b/src/OnExtract.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "dat2.h" +#include "Util.h" + +int OnExtract() +{ + CFileDescriptorBase descriptor; + DWORD dwTotalExtracted = 0; + CString strDirectory; + CString strFileName; + int nLastSlashPos; + CString strOutFileName; + CFile fileOut; + + // Create output directory + if (!g_strOutFolder.IsEmpty()) { + if (!ForceCreateDirectory(g_strOutFolder)) { + printf("Error: Unable create output directory\n"); + return 3; + } + } + + for(INT_PTR j = 0; j < g_FileList.GetCount(); j++) { + if (g_pDatFile->FindFirst(g_FileList[j]) != -1) { + do { + descriptor = (*g_pDatFile)[g_pDatFile->GetCurrentFileIndex()]; + printf("Extracting: %s", descriptor.m_strFileName); + nLastSlashPos = descriptor.m_strFileName.ReverseFind('\\'); + strDirectory = descriptor.m_strFileName.Left(nLastSlashPos + 1); + strFileName = descriptor.m_strFileName.Right(descriptor.m_strFileName.GetLength() - nLastSlashPos - 1); + + + if (g_bExtractWithoutPaths) { + strOutFileName = g_strOutFolder + strFileName; + } + else { + if (!ForceCreateDirectory(g_strOutFolder + strDirectory)) { + return 3; + } + + strOutFileName = g_strOutFolder + strDirectory + strFileName; + } + + if (fileOut.Open(strOutFileName, CFile::modeCreate | CFile::modeWrite)) { + CArchive arOut(&fileOut, CArchive::store); + + if (g_pDatFile->Inflate(arOut)) { + dwTotalExtracted++; + printf("\n"); + } + else { + printf(" Error!!!\n"); + return 3; + } + } + else { + printf(" Error!!! Unable open out file %s\n", strOutFileName); + return 3; + } + + fileOut.Close(); + } while(g_pDatFile->FindNext() != -1); + } + } + + printf("----------\n"); + printf("%u file(s) extracted\n", dwTotalExtracted); + return 0; +} \ No newline at end of file diff --git a/src/OnList.cpp b/src/OnList.cpp new file mode 100644 index 0000000..8e032c8 --- /dev/null +++ b/src/OnList.cpp @@ -0,0 +1,28 @@ +#include "stdafx.h" +#include "dat2.h" + +int OnList() +{ + CFileDescriptorBase descriptor; + DWORD dwTotalSize = 0; + + printf(" Length Packed Type Name\n"); + printf(" ---------- ----------- -------- ---------------\n"); + + for(INT_PTR i = 0; i < g_pDatFile->GetFileCount(); i++) { + descriptor = (*g_pDatFile)[i]; + dwTotalSize += descriptor.m_dwFileLength; + printf("%11u %11u %s %s\n", + descriptor.m_dwFileLength, + descriptor.m_dwPackedFileLength, + (descriptor.m_nType) ? "Packed" : "Stored", + descriptor.m_strFileName); + } + + printf(" ---------- ---------------\n"); + printf("%11u %u file(s)\n", + dwTotalSize, + DWORD(g_pDatFile->GetFileCount())); + + return 0; +} \ No newline at end of file diff --git a/src/OnShrink.cpp b/src/OnShrink.cpp new file mode 100644 index 0000000..71b1e65 --- /dev/null +++ b/src/OnShrink.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "dat2.h" + + +DWORD dwTotalMoved = 0; + +void OutInfo(INT_PTR nIndex) +{ + CFileDescriptorBase descriptor; + descriptor = (*g_pDatFile)[nIndex]; + + + dwTotalMoved++; + printf("Moving: %s\n", descriptor.m_strFileName); +} + + +int OnShrink() +{ + DWORD dwOldLength = g_pDatFile->GetLength(); + + if (g_pDatFile->Shrink(OutInfo)) { + printf("----------\n"); + printf("%u file(s) moved. Old size: %u bytes. New size: %u bytes \n", dwTotalMoved, dwOldLength, g_pDatFile->GetLength()); + return 0; + } + else { + printf("Error: Unable shrink file\n"); + return 5; + } +} \ No newline at end of file diff --git a/src/Resource.h b/src/Resource.h new file mode 100644 index 0000000..65f5d14 --- /dev/null +++ b/src/Resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by dat2.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..25de972 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,177 @@ +#include "stdafx.h" +#include "Util.h" + +CString ExtractFilePath(const CString& strFileName, BOOL bIncludeDrive) +{ + CString strDrive; + CString strDirectory; + LPTSTR lpszDrive = strDrive.GetBufferSetLength(_MAX_DRIVE); + LPTSTR lpszDirectory = strDirectory.GetBufferSetLength(_MAX_DIR); + _splitpath(strFileName, lpszDrive, lpszDirectory, NULL, NULL); + strDrive.ReleaseBuffer(); + strDirectory.ReleaseBuffer(); + + if (bIncludeDrive) { + return (strDrive + strDirectory); + } + else { + return strDirectory; + } +} + +CString ExtractFileName(const CString& strFileName) +{ + CString strName; + CString strExtension; + LPTSTR lpszName = strName.GetBufferSetLength(_MAX_FNAME); + LPTSTR lpszExtension = strExtension.GetBufferSetLength(_MAX_EXT); + _splitpath(strFileName, NULL, NULL, lpszName, lpszExtension); + strName.ReleaseBuffer(); + strExtension.ReleaseBuffer(); + return (strName + strExtension); +} + +BOOL DirectoryExist(const CString& strDirectory) +{ + DWORD dwResult = ::GetFileAttributes(strDirectory); + return ((dwResult != INVALID_FILE_ATTRIBUTES) && + ((dwResult & FILE_ATTRIBUTE_DIRECTORY) != 0)); +} + +BOOL ForceCreateDirectory(CString strDirectory) +{ + if (strDirectory.GetLength() == 0) { + return TRUE; + } + + if (strDirectory.Right(1) == _T("\\")) { + strDirectory = strDirectory.Left(strDirectory.GetLength() - 1); + } + + if ((strDirectory.GetLength() < 3) || + DirectoryExist(strDirectory) || + (ExtractFilePath(strDirectory) == strDirectory)) { + return TRUE; + } + + return (ForceCreateDirectory(ExtractFilePath(strDirectory)) && + ::CreateDirectory(strDirectory, NULL)); +} + +UINT ReadMSBWord(CArchive& ar, WORD& wValue) +{ + BYTE* pBuffer = reinterpret_cast(&wValue); + return (ar.Read(pBuffer + 1, 1) + ar.Read(pBuffer, 1)); +} + +UINT ReadMSBDWord(CArchive& ar, DWORD& dwValue) +{ + BYTE* pBuffer = reinterpret_cast(&dwValue); + return (ar.Read(pBuffer + 3, 1) + ar.Read(pBuffer + 2, 1) + + ar.Read(pBuffer + 1, 1) + ar.Read(pBuffer, 1)); +} + +UINT ReadMSBWord(CFile& file, WORD& wValue) +{ + BYTE* pBuffer = reinterpret_cast(&wValue); + return (file.Read(pBuffer + 1, 1) + file.Read(pBuffer, 1)); +} + +UINT ReadMSBDWord(CFile& file, DWORD& dwValue) +{ + BYTE* pBuffer = reinterpret_cast(&dwValue); + return (file.Read(pBuffer + 3, 1) + file.Read(pBuffer + 2, 1) + + file.Read(pBuffer + 1, 1) + file.Read(pBuffer, 1)); +} + +BOOL ReadString(CArchive& ar, CString& strString) +{ + BYTE nStrLength; + + if (ar.Read(&nStrLength, 1) != 1) { + return FALSE; + } + + LPTSTR lpszString = strString.GetBufferSetLength(nStrLength + 1); + DWORD dwRead = ar.Read(lpszString, nStrLength); + strString.ReleaseBuffer(nStrLength); + + if (dwRead != nStrLength) { + return FALSE; + } + else { + return TRUE; + } +} + +BOOL ReadString(CFile& file, CString& strString) +{ + BYTE nStrLength; + + if (file.Read(&nStrLength, 1) != 1) { + return FALSE; + } + + LPTSTR lpszString = strString.GetBufferSetLength(nStrLength + 1); + DWORD dwRead = file.Read(lpszString, nStrLength); + strString.ReleaseBuffer(nStrLength); + + if (dwRead != nStrLength) { + return FALSE; + } + else { + return TRUE; + } +} + +void WriteMSBWord(CArchive& ar, WORD wValue) +{ + BYTE* pBuffer = reinterpret_cast(&wValue); + + ar.Write(pBuffer + 1, 1); + ar.Write(pBuffer, 1); +} + +void WriteMSBDWord(CArchive& ar, DWORD dwValue) +{ + BYTE* pBuffer = reinterpret_cast(&dwValue); + + ar.Write(pBuffer + 3, 1); + ar.Write(pBuffer + 2, 1); + ar.Write(pBuffer + 1, 1); + ar.Write(pBuffer, 1); +} + +void WriteMSBWord(CFile& file, WORD wValue) +{ + BYTE* pBuffer = reinterpret_cast(&wValue); + + file.Write(pBuffer + 1, 1); + file.Write(pBuffer, 1); +} + +void WriteMSBDWord(CFile& file, DWORD dwValue) +{ + BYTE* pBuffer = reinterpret_cast(&dwValue); + + file.Write(pBuffer + 3, 1); + file.Write(pBuffer + 2, 1); + file.Write(pBuffer + 1, 1); + file.Write(pBuffer, 1); +} + +void WriteString(CArchive& ar, CString& strString) +{ + BYTE nStrLength = BYTE(strString.GetLength()); + + ar.Write(&nStrLength, 1); + ar.Write(LPCTSTR(strString), nStrLength); +} + +void WriteString(CFile& file, CString& strString) +{ + BYTE nStrLength = BYTE(strString.GetLength()); + + file.Write(&nStrLength, 1); + file.Write(LPCTSTR(strString), nStrLength); +} diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 0000000..35d8c4d --- /dev/null +++ b/src/Util.h @@ -0,0 +1,24 @@ +#pragma once + +CString ExtractFileName(const CString& strFileName); +CString ExtractFilePath(const CString& strFileName, BOOL bIncludeDrive = TRUE); +BOOL DirectoryExist(const CString& strDirectory); +BOOL ForceCreateDirectory(CString strDirectory); + +UINT ReadMSBWord(CArchive& ar, WORD& wValue); +UINT ReadMSBDWord(CArchive& ar, DWORD& dwValue); + +UINT ReadMSBWord(CFile& file, WORD& wValue); +UINT ReadMSBDWord(CFile& file, DWORD& dwValue); + +BOOL ReadString(CArchive& ar, CString& strString); +BOOL ReadString(CFile& file, CString& strString); + +void WriteMSBWord(CArchive& ar, WORD wValue); +void WriteMSBDWord(CArchive& ar, DWORD dwValue); + +void WriteMSBWord(CFile& file, WORD wValue); +void WriteMSBDWord(CFile& file, DWORD dwValue); + +void WriteString(CArchive& ar, CString& strString); +void WriteString(CFile& file, CString& strString); diff --git a/src/XGetopt.cpp b/src/XGetopt.cpp new file mode 100644 index 0000000..0ba3d7a --- /dev/null +++ b/src/XGetopt.cpp @@ -0,0 +1,219 @@ +// XGetopt.cpp Version 1.2 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// Description: +// XGetopt.cpp implements getopt(), a function to parse command lines. +// +// History +// Version 1.2 - 2003 May 17 +// - Added Unicode support +// +// Version 1.1 - 2002 March 10 +// - Added example to XGetopt.cpp module header +// +// This software is released into the public domain. +// You are free to use it in any way you like. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// if you are using precompiled headers then include this line: +#include "stdafx.h" +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// if you are not using precompiled headers then include these lines: +//#include +//#include +//#include +/////////////////////////////////////////////////////////////////////////////// + + +#include "XGetopt.h" + + +/////////////////////////////////////////////////////////////////////////////// +// +// X G e t o p t . c p p +// +// +// NAME +// getopt -- parse command line options +// +// SYNOPSIS +// int getopt(int argc, TCHAR *argv[], TCHAR *optstring) +// +// extern TCHAR *optarg; +// extern int optind; +// +// DESCRIPTION +// The getopt() function parses the command line arguments. Its +// arguments argc and argv are the argument count and array as +// passed into the application on program invocation. In the case +// of Visual C++ programs, argc and argv are available via the +// variables __argc and __argv (double underscores), respectively. +// getopt returns the next option letter in argv that matches a +// letter in optstring. (Note: Unicode programs should use +// __targv instead of __argv. Also, all character and string +// literals should be enclosed in _T( ) ). +// +// optstring is a string of recognized option letters; if a letter +// is followed by a colon, the option is expected to have an argument +// that may or may not be separated from it by white space. optarg +// is set to point to the start of the option argument on return from +// getopt. +// +// Option letters may be combined, e.g., "-ab" is equivalent to +// "-a -b". Option letters are case sensitive. +// +// getopt places in the external variable optind the argv index +// of the next argument to be processed. optind is initialized +// to 0 before the first call to getopt. +// +// When all options have been processed (i.e., up to the first +// non-option argument), getopt returns EOF, optarg will point +// to the argument, and optind will be set to the argv index of +// the argument. If there are no non-option arguments, optarg +// will be set to NULL. +// +// The special option "--" may be used to delimit the end of the +// options; EOF will be returned, and "--" (and everything after it) +// will be skipped. +// +// RETURN VALUE +// For option letters contained in the string optstring, getopt +// will return the option letter. getopt returns a question mark (?) +// when it encounters an option letter not included in optstring. +// EOF is returned when processing is finished. +// +// BUGS +// 1) Long options are not supported. +// 2) The GNU double-colon extension is not supported. +// 3) The environment variable POSIXLY_CORRECT is not supported. +// 4) The + syntax is not supported. +// 5) The automatic permutation of arguments is not supported. +// 6) This implementation of getopt() returns EOF if an error is +// encountered, instead of -1 as the latest standard requires. +// +// EXAMPLE +// BOOL CMyApp::ProcessCommandLine(int argc, TCHAR *argv[]) +// { +// int c; +// +// while ((c = getopt(argc, argv, _T("aBn:"))) != EOF) +// { +// switch (c) +// { +// case _T('a'): +// TRACE(_T("option a\n")); +// // +// // set some flag here +// // +// break; +// +// case _T('B'): +// TRACE( _T("option B\n")); +// // +// // set some other flag here +// // +// break; +// +// case _T('n'): +// TRACE(_T("option n: value=%d\n"), atoi(optarg)); +// // +// // do something with value here +// // +// break; +// +// case _T('?'): +// TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]); +// return FALSE; +// break; +// +// default: +// TRACE(_T("WARNING: no handler for option %c\n"), c); +// return FALSE; +// break; +// } +// } +// // +// // check for non-option args here +// // +// return TRUE; +// } +// +/////////////////////////////////////////////////////////////////////////////// + +TCHAR *optarg; // global argument pointer +int optind = 0; // global argv index + +int getopt(int argc, TCHAR *argv[], TCHAR *optstring) +{ + static TCHAR *next = NULL; + if (optind == 0) + next = NULL; + + optarg = NULL; + + if (next == NULL || *next == _T('\0')) + { + if (optind == 0) + optind++; + + if (optind >= argc || argv[optind][0] != _T('-') || argv[optind][1] == _T('\0')) + { + optarg = NULL; + if (optind < argc) + optarg = argv[optind]; + return EOF; + } + + if (_tcscmp(argv[optind], _T("--")) == 0) + { + optind++; + optarg = NULL; + if (optind < argc) + optarg = argv[optind]; + return EOF; + } + + next = argv[optind]; + next++; // skip past - + optind++; + } + + TCHAR c = *next++; + TCHAR *cp = _tcschr(optstring, c); + + if (cp == NULL || c == _T(':')) + return _T('?'); + + cp++; + if (*cp == _T(':')) + { + if (*next != _T('\0')) + { + optarg = next; + next = NULL; + } + else if (optind < argc) + { + optarg = argv[optind]; + optind++; + } + else + { + return _T('?'); + } + } + + return c; +} diff --git a/src/XGetopt.h b/src/XGetopt.h new file mode 100644 index 0000000..7e75f26 --- /dev/null +++ b/src/XGetopt.h @@ -0,0 +1,23 @@ +// XGetopt.h Version 1.2 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// This software is released into the public domain. +// You are free to use it in any way you like. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XGETOPT_H +#define XGETOPT_H + +extern int optind, opterr; +extern TCHAR *optarg; + +int getopt(int argc, TCHAR *argv[], TCHAR *optstring); + +#endif //XGETOPT_H diff --git a/src/dat2.cpp b/src/dat2.cpp new file mode 100644 index 0000000..db70ff6 --- /dev/null +++ b/src/dat2.cpp @@ -0,0 +1,320 @@ +// dat2.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include "dat2.h" +#include "DatFile1.h" +#include "DatFile2.h" +#include "XGetopt.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif /* _DEBUG */ + +// Globals +Command g_Command = COMMAND_UNKNOWN; +int g_nCompressMethod = 9; +BOOL g_bRecurseIntoDir = FALSE; +BOOL g_bExtractWithoutPaths = FALSE; +CString g_strOutFolder; +CString g_strDatFileName; +CStringArray g_FileList; +BOOL g_bFallout1Dat = FALSE; +CString g_strTargetDir = ""; + +// Dat-file +CDatFile1 g_DatFile1; +CDatFile2 g_DatFile2; +CDatFileBase* g_pDatFile = NULL; + +// Functions +void PrintUsage(char* lpszFileName); +int ParseCommandLine(int argc, char* argv[]); + +// The one and only application object +CWinApp theApp; + +int main(int argc, char* argv[]) +{ + if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { + printf("Fatal Error: MFC initialization failed\n"); + return 1; + } + else { + printf("Fallout DAT-files packer/unpacker, version 2.32\n"); + printf("Copyright (C) Anchorite (TeamX), 2004-2006\n"); + printf("anchorite2001@yandex.ru\n"); + printf("\n"); + +// for(int i = 0; i < argc; i++) { +// printf("argv[%d] = %s\n", i, argv[i]); +// } + + if (argc < 3) { + PrintUsage(argv[0]); + return 0; + } + + int nResult = ParseCommandLine(argc, argv); + + switch(nResult) { + case -1: { + printf("Error: Invalid command\n"); + return 1; + } + + case -2: { + printf("Error: Name of output directory is omitted\n"); + return 1; + } + + case -3: { + printf("Error: Name of dat-file is omitted\n"); + return 1; + } + + case -4: { + printf("Error: List of files is omitted\n"); + return 1; + } + + case -5: { + printf("Error: Unable open response file\n"); + return 1; + } + } + + nResult = 0; + UINT nOpenMode; + + switch(g_Command) { + case COMMAND_EXTRACT: + nOpenMode = CFile::modeRead | CFile::shareDenyWrite; + break; + + case COMMAND_DELETE: + nOpenMode = CFile::modeReadWrite | CFile::shareDenyWrite; + break; + + case COMMAND_LIST: + nOpenMode = CFile::modeRead | CFile::shareDenyWrite; + break; + + case COMMAND_SHRINK: + nOpenMode = CFile::modeReadWrite | CFile::shareDenyWrite; + } + + if (g_Command == COMMAND_ADD) { + if (g_bFallout1Dat) { + g_pDatFile = &g_DatFile1; + } + else { + g_pDatFile = &g_DatFile2; + } + } + else { + if (g_DatFile2.Open(g_strDatFileName, nOpenMode)) { + g_pDatFile = &g_DatFile2; + } + else if (g_DatFile1.Open(g_strDatFileName, nOpenMode)) { + g_pDatFile = &g_DatFile1; + } + else { + printf("Error: Unable open file %s\n", LPCTSTR(g_strDatFileName)); + return 1; + } + } + + switch(g_Command) { + case COMMAND_ADD: + nResult = OnAdd(); + break; + + case COMMAND_EXTRACT: + nResult = OnExtract(); + break; + + case COMMAND_DELETE: + nResult = OnDelete(); + break; + + case COMMAND_LIST: + nResult = OnList(); + break; + + case COMMAND_SHRINK: + nResult = OnShrink(); + } + + printf("\n"); + printf("Flushing buffers...\n"); + g_pDatFile->Flush(); + + return nResult; + } +} + +void PrintUsage(char* lpszFileName) +{ + printf("Usage: %s [options] [-t dir] [-d dir] dat-file [list | @response-file]\n", lpszFileName); + printf("\n"); + printf("Commands\n"); + printf(" a: Add files to dat-file. Create new if dat-file not exist\n"); + printf(" x: Extract files from dat-file\n"); + printf(" d: Delete files from dat-file (only info about files)\n"); + printf(" l: List files in dat-file\n"); + printf(" k: Shrink dat-file\n"); + printf("\n"); + printf("Options\n"); + printf(" -s: create Fallout 1 dat-file\n"); + printf(" -r: recurse into directories\n"); + printf(" -0..9: Compression method\n"); + printf(" (Fallout1: 0 - store, other numbers - compress (default)\n"); + printf(" (Fallout2: 0 - store, 1 - best speed, 9 - best compression (default)\n"); + printf(" -p: extract without paths\n"); + printf(" -d: extract files into specified directory\n"); + printf(" -t: add files to specified directory of dat-file\n"); + printf(" --: end of options\n"); + + printf("\n"); +} + +int ParseCommandLine(int argc, char* argv[]) +{ + // Command + if (memcmp(argv[1], "a", lstrlen(argv[1])) == 0) { + g_Command = COMMAND_ADD; + } + else if (memcmp(argv[1], "x", lstrlen(argv[1])) == 0) { + g_Command = COMMAND_EXTRACT; + } + else if (memcmp(argv[1], "d", lstrlen(argv[1])) == 0) { + g_Command = COMMAND_DELETE; + } + else if (memcmp(argv[1], "l", lstrlen(argv[1])) == 0) { + g_Command = COMMAND_LIST; + } + else if (memcmp(argv[1], "k", lstrlen(argv[1])) == 0) { + g_Command = COMMAND_SHRINK; + } + else { + return -1; + } + + // Options + argc--; + argv++; + + int c; + + while((c = getopt(argc, argv, "r0123456789pd:st:")) != EOF) { + switch(c) { + case 'r': + g_bRecurseIntoDir = TRUE; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + g_nCompressMethod = c - '0'; + break; + + case 'p': + g_bExtractWithoutPaths = TRUE; + break; + + case 'd': + g_strOutFolder = optarg; + + if ((!g_strOutFolder.IsEmpty()) && (g_strOutFolder.Right(1) != "\\")) { + g_strOutFolder += "\\"; + } + + break; + + case 's': + g_bFallout1Dat = TRUE; + break; + + case 't': + g_strTargetDir = optarg; + + if ((!g_strTargetDir.IsEmpty()) && (g_strTargetDir.Right(1) != "\\")) { + g_strTargetDir += "\\"; + } + + break; + + case '?': + return -2; + + default: // No more options + break; + } + } + + // Dat-file + if (optind < argc) { + g_strDatFileName = argv[optind]; + } + + if (g_strDatFileName.IsEmpty()) { + return -3; + } + + if ((g_strDatFileName.Right(4).MakeLower() != ".dat") || (g_strDatFileName == ".dat")) { + g_strDatFileName += ".dat"; + } + + // List of files + CString strArgument; + + for(int i = optind + 1; i < argc; i++) { + strArgument = argv[i]; + + int nArglength = strArgument.GetLength(); + + if ((i == optind + 1) && (nArglength > 1) && (strArgument[0] == '@')) { + CStdioFile fileResponse; + + if (!fileResponse.Open(strArgument.Right(nArglength - 1), CFile::modeRead | CFile::shareDenyWrite | CFile::typeText)) { + return -5; + }; + + CArchive arResponse(&fileResponse, CArchive::load); + + while(arResponse.ReadString(strArgument)) { + if (!strArgument.IsEmpty()) { + g_FileList.Add(strArgument); + } + } + + break; + } + else { + g_FileList.Add(strArgument); + } + } + + if ((g_FileList.GetSize() == 0)) { + if (g_Command == COMMAND_EXTRACT) { + g_FileList.Add("*"); + } + else if ((g_Command != COMMAND_LIST) && (g_Command != COMMAND_SHRINK)) { + return -4; + } + } + +// for(int i = 0; i < g_FileList.GetSize(); i++) { +// printf("g_FileList[%d] = %s\n", i, g_FileList[i]); +// } + + return 0; +} \ No newline at end of file diff --git a/src/dat2.h b/src/dat2.h new file mode 100644 index 0000000..a28b82e --- /dev/null +++ b/src/dat2.h @@ -0,0 +1,31 @@ +#pragma once + +#include "DatFileBase.h" +#include "resource.h" + +// Commands +enum Command { + COMMAND_UNKNOWN, + COMMAND_ADD, + COMMAND_EXTRACT, + COMMAND_DELETE, + COMMAND_LIST, + COMMAND_SHRINK +}; + +// Globals +extern Command g_Command; +extern int g_nCompressMethod; +extern BOOL g_bRecurseIntoDir; +extern BOOL g_bExtractWithoutPaths; +extern CString g_strOutFolder; +extern CString g_strDatFileName; +extern CStringArray g_FileList; +extern CDatFileBase* g_pDatFile; + +// Functions +int OnAdd(); +int OnExtract(); +int OnDelete(); +int OnList(); +int OnShrink(); diff --git a/src/dat2.rc b/src/dat2.rc new file mode 100644 index 0000000..d61738f --- /dev/null +++ b/src/dat2.rc @@ -0,0 +1,114 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +#ifdef _WIN32 +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Russian resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,3,2,0 + PRODUCTVERSION 2,3,2,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Anchorite (TeamX)" + VALUE "FileDescription", "Fallout dat-file packer/unpacker" + VALUE "FileVersion", "2, 3, 2, 0" + VALUE "InternalName", "Fallout dat-file packer/unpacker" + VALUE "LegalCopyright", "Copyright (C) Anchorite (TeamX), 2004-2006" + VALUE "OriginalFilename", "dat2.exe" + VALUE "ProductName", "Fallout dat-file packer/unpacker" + VALUE "ProductVersion", "2, 3, 2, 0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/dat2.sln b/src/dat2.sln new file mode 100644 index 0000000..89698d6 --- /dev/null +++ b/src/dat2.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dat2", "dat2.vcproj", "{00920866-F659-4AF7-87AF-FE86A97044CC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00920866-F659-4AF7-87AF-FE86A97044CC}.Debug|Win32.ActiveCfg = Debug|Win32 + {00920866-F659-4AF7-87AF-FE86A97044CC}.Debug|Win32.Build.0 = Debug|Win32 + {00920866-F659-4AF7-87AF-FE86A97044CC}.Release|Win32.ActiveCfg = Release|Win32 + {00920866-F659-4AF7-87AF-FE86A97044CC}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/dat2.vcproj b/src/dat2.vcproj new file mode 100644 index 0000000..db6c74b --- /dev/null +++ b/src/dat2.vcproj @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/stdafx.cpp b/src/stdafx.cpp new file mode 100644 index 0000000..dd4fd0b --- /dev/null +++ b/src/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// dat2.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/src/stdafx.h b/src/stdafx.h new file mode 100644 index 0000000..a28a211 --- /dev/null +++ b/src/stdafx.h @@ -0,0 +1,19 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#define WINVER 0x0410 +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif + +#include +#include // MFC core and standard components +#include // MFC extensions + +#include