initial commit

This commit is contained in:
2025-09-01 20:47:58 +08:00
commit 12c51b5ddb
843 changed files with 475321 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
set(LIBNPK_SOURCE_FILES
libNPK.h
libNPK_Utils.h
./source/libNPK.cpp
./source/libNPK_StdAfx.h
./source/libNPK_Errors.h
./source/libNPK_Errors.cpp
./source/libNPK_NPKFile.h
./source/libNPK_NPKFile.cpp
./source/libNPK_Utils.cpp
./source/libNPK_FileStream.h
./source/libNPK_FileStream.cpp
)
add_library(libNPK STATIC
${LIBNPK_SOURCE_FILES}
)
target_include_directories(libNPK
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
../libTinyEncrypt
)
+86
View File
@@ -0,0 +1,86 @@
#pragma once
namespace NPK
{
//------------------------------------
//pre-dfine
//------------------------------------
class INPKFile;
class IFileStream;
//------------------------------------
// NPK file error codes
//------------------------------------
enum ErrorCode
{
NPK_ERR_SUCCESS = 0,
NPK_ERR_FILE_NOT_FOUND,
NPK_ERR_INVALID_FILE_FORMAT,
NPK_ERR_INVALID_FILE_HASH,
NPK_ERR_FILE_ACCESS,
NPK_ERR_FILE_READ,
NPK_ERR_FILE_WRITE,
NPK_ERR_PARAM,
NPK_ERR_INVALID_STATE,
//Other errors
NPK_ERR_UNKNOWN
};
//------------------------------------
//API for NPK file
//------------------------------------
// Create a NPK file interface
INPKFile* createNPKFile();
// destroy an NPK interface
void closeNPKFile(INPKFile* npkFile);
// Get the last error code
ErrorCode getLastErrorCode();
// Get the last error message
const char* getLastErrorMessage();
//------------------------------------
// define the NPK file interface
//------------------------------------
class INPKFile
{
public:
struct FileNode
{
std::string fileName;
uint32_t fileOffset = 0;
uint32_t fileSize = 0;
};
public:
virtual bool openPakFile(const char* szPackFileName) = 0;
virtual bool closePakFile(void) = 0;
virtual int32_t getFileCount(void) const = 0;
virtual bool getFileNode(int32_t index, FileNode& fileNode) const = 0;
virtual IFileStream* openFileStream(const char* szFileName) = 0;
virtual void closeFileStream(IFileStream* fileStream) = 0;
};
class IFileStream
{
public:
virtual const char* name(void) const = 0;
virtual uint32_t size(void) const = 0;
virtual uint32_t read(void* buffer, uint32_t size) = 0;
virtual bool seek(uint32_t offset) = 0;
virtual uint32_t tell(void) const = 0;
virtual uint32_t crc32(void) = 0;
virtual bool isEOF(void) const = 0;
};
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <string>
// Convert a size in bytes to a human-readable string
std::string sizeToString(int64_t size);
// Convert a size string (e.g., "10k", "5M", "2G") to an integer size in bytes
int64_t sizeFromString(const char* sizeStr);
// Compare two file names, considering both long and short paths
int compareTwoFileName(const char* szFileName1, const char* szFileName2);
// Convert a standard ANSI string to a wide string (UTF-16)
std::wstring convertAnsiStringToWide(const char* szSource);
// Read a file into a buffer and return the buffer pointer.
uint8_t* readFileToBuffer(const char* filename, int64_t& outSize);
// Free the file buffer allocated by readFileToBuffer
void freeFileBuffer(uint8_t* buffer);
// Clears the specified path by deleting all files and optionally removing the directory itself.
bool clearPath(const char* szPath, bool recursion, bool removeSelf);
//Creates an empty directory at the specified path. If the path already exists and is a directory, clears its contents.
bool createEmptyPath(const char* szPath);
// Force create a directory path, ensuring all parent directories are created as needed.
bool forceCreatePath(const char* szPath);
// Get the parent directory path of the specified path.
void getParentPath(char* szPath);
+6
View File
@@ -0,0 +1,6 @@
#include "libNPK_StdAfx.h"
namespace NPK
{
}
+36
View File
@@ -0,0 +1,36 @@
#include "libNPK_StdAfx.h"
#include "libNPK_Errors.h"
namespace NPK
{
static ErrorCode g_LastError = NPK_ERR_SUCCESS;
static char g_strLastErrorDesc[1024] = {0};
//设置错误
void setLastError(ErrorCode err, const char* desc, ...)
{
g_LastError = err;
g_strLastErrorDesc[0] = 0;
va_list ptr;
va_start(ptr, desc);
_vsnprintf(g_strLastErrorDesc, 1024, desc, ptr);
va_end(ptr);
}
//得到上一个错误
ErrorCode getLastErrorCode(void)
{
return g_LastError;
}
const char* getLastErrorMessage(void)
{
return g_strLastErrorDesc;
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "libNPK.h"
namespace NPK
{
void setLastError(ErrorCode err, const char* desc = "", ...);
}
+87
View File
@@ -0,0 +1,87 @@
#include "libNPK_StdAfx.h"
#include "libNPK_FileStream.h"
#include "libNPK_Errors.h"
#include "libEncrypt_CRC32.h"
namespace NPK
{
CFileStream::CFileStream(CNPKFile* pNPKFile, HANDLE hFile, const INPKFile::FileNode& fileNode)
: m_pNPKFile(pNPKFile)
, m_hFileandle(hFile)
, m_fileName(fileNode.fileName)
, m_fileOffset(fileNode.fileOffset)
, m_fileSize(fileNode.fileSize)
, m_currentOffset(0)
{
}
CFileStream::~CFileStream()
{
}
uint32_t CFileStream::read(void* buffer, uint32_t size)
{
if(isEOF())
{
return 0; // End of file
}
DWORD dwOffset = m_fileOffset + m_currentOffset;
if (dwOffset != ::SetFilePointer(m_hFileandle, dwOffset, NULL, FILE_BEGIN))
{
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
return 0; // Failed to set file pointer
}
DWORD dwRemainBytes = m_fileSize - m_currentOffset;
DWORD dwToRead = (size < dwRemainBytes) ? size : dwRemainBytes;
DWORD dwBytesRead = 0;
if(!::ReadFile(m_hFileandle, buffer, dwToRead, &dwBytesRead, NULL))
{
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
return 0; // Failed to read file
}
m_currentOffset += dwBytesRead;
return dwBytesRead;
}
bool CFileStream::seek(uint32_t offset)
{
if(offset > m_fileSize)
{
setLastError(NPK_ERR_FILE_READ, "Seek offset %u exceeds file size %u", offset, m_fileSize);
return false; // Seek offset exceeds file size
}
m_currentOffset = offset;
return true;
}
uint32_t CFileStream::crc32(void)
{
if (m_crc32Calculated) return m_crc32; // CRC32 already calculated
const size_t kBufferSize = 1024*4;
uint8_t pTempBuffer[kBufferSize];
uint32_t currentOffset = m_currentOffset;
m_currentOffset = 0;
m_crc32 = 0;
// Read the entire file to calculate CRC32
uint32_t bytesRead = 0;
while ((bytesRead = read(pTempBuffer, kBufferSize)) > 0)
{
m_crc32 = crcMemory32(pTempBuffer, bytesRead, m_crc32);
}
m_currentOffset = currentOffset;
m_crc32Calculated = true;
return m_crc32;
}
}
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include "libNPK.h"
#include "libNPK_NPKFile.h"
namespace NPK
{
class CFileStream : public IFileStream
{
public:
virtual const char* name(void) const {
return m_fileName.c_str();
}
virtual uint32_t size(void) const {
return m_fileSize;
}
virtual uint32_t read(void* buffer, uint32_t size);
virtual bool seek(uint32_t offset);
virtual uint32_t tell(void) const {
return m_currentOffset;
}
virtual uint32_t crc32(void);
virtual bool isEOF(void) const {
return m_currentOffset >= m_fileSize;
}
protected:
//Pack file
CNPKFile* m_pNPKFile;
// windows file handle
HANDLE m_hFileandle;
std::string m_fileName;
//offset of the file in the pak file
uint32_t m_fileOffset = 0;
//size of the file
uint32_t m_fileSize = 0;
//current read offset in the file
uint32_t m_currentOffset = 0;
//CRC32 checksum of the file
uint32_t m_crc32 = 0;
//Flag to indicate if CRC32 has been calculated
bool m_crc32Calculated = false;
private:
CFileStream(CNPKFile* pNPKFile, HANDLE hFile, const INPKFile::FileNode& fileNode);
virtual ~CFileStream();
friend IFileStream* CNPKFile::openFileStream(const char* szFileName);
friend void CNPKFile::closeFileStream(IFileStream* fileStream);
};
}
+269
View File
@@ -0,0 +1,269 @@
#include "libNPK_StdAfx.h"
#include "libNPK_NPKFile.h"
#include "libNPK_Errors.h"
#include "libEncrypt_SHA256.h"
#include "libNPK_FileStream.h"
#include <assert.h>
namespace NPK
{
const char* CNPKFile::kNPKFileHeaderMagic = "NeoplePack_Bill\0";
const char* CNPKFile::kNPKFileNameXorCode = "puchikon@neople dungeon and fighter DNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNF";
CNPKFile::CNPKFile()
: m_hPakFile(NULL)
, m_nFileCounts(0)
{
}
CNPKFile::~CNPKFile()
{
}
bool CNPKFile::openPakFile(const char* szPackFileName)
{
assert(szPackFileName);
if (!szPackFileName || szPackFileName[0] == 0)
{
setLastError(NPK_ERR_PARAM);
return false;
}
//close current pak file if it is open
closePakFile();
m_strPackFileName = szPackFileName;
//open the pak file
m_hPakFile = ::CreateFile(szPackFileName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_SEQUENTIAL_SCAN,
0);
if (m_hPakFile == INVALID_HANDLE_VALUE)
{
setLastError(NPK_ERR_FILE_ACCESS, "File=%s, WinErr=%d", szPackFileName, ::GetLastError());
return false;
}
NPKFileHeader fileHeader;
SecureZeroMemory(&fileHeader, sizeof(fileHeader));
//Read NPK Header
DWORD dwReadBytes;
if (!ReadFile(m_hPakFile, &fileHeader, sizeof(fileHeader), &dwReadBytes, 0) ||
dwReadBytes != sizeof(fileHeader))
{
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
return false;
}
//Check NPK Magic Header
if(memcmp(fileHeader.szFileHeadMagic, kNPKFileHeaderMagic, kNPKFileHeaderMagicSize) != 0)
{
setLastError(NPK_ERR_INVALID_FILE_FORMAT, "File=%s, Magic=%s", szPackFileName, fileHeader.szFileHeadMagic);
return false;
}
//get file counts
m_nFileCounts = fileHeader.nFileCounts;
m_fileList.resize(m_nFileCounts);
// read file info
FileInfo fileInfo;
for (int32_t i = 0; i < m_nFileCounts; i++)
{
SecureZeroMemory(&fileInfo, sizeof(fileInfo));
if (!ReadFile(m_hPakFile, &fileInfo, sizeof(fileInfo), &dwReadBytes, 0) ||
dwReadBytes != sizeof(fileInfo))
{
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
return false;
}
//decode file name
for(int32_t j = 0; j < kNPKFileNameXorCodeSize; j++)
{
fileInfo.szFileName[j] ^= kNPKFileNameXorCode[j];
}
m_fileList[i].fileName = fileInfo.szFileName;
m_fileList[i].fileOffset = fileInfo.fileOffset;
m_fileList[i].fileSize = fileInfo.fileSize;
}
if(!checkPakFileHash())
{
// if hash check failed, close the pak file
closePakFile();
return false;
}
//build hash file list
buildHashFileList();
return true;
}
bool CNPKFile::checkPakFileHash()
{
if(m_hPakFile == NULL)
{
setLastError(NPK_ERR_INVALID_STATE, "NPK file is not opened");
return false;
}
//calculate hash
int32_t headSize = sizeof(NPKFileHeader) + sizeof(FileInfo) * m_nFileCounts;
int32_t hashSize = (int32_t)(headSize / 17) * 17; // align to 17 bytes
// allocate hash buffer
uint8_t* hashBuffer = new uint8_t[hashSize];
SecureZeroMemory(hashBuffer, hashSize);
// copy header and file info to hash buffer
DWORD dwReadBytes = 0;
if (0 != ::SetFilePointer(m_hPakFile, 0, 0, FILE_BEGIN) ||
!ReadFile(m_hPakFile, hashBuffer, hashSize, &dwReadBytes, 0) ||
dwReadBytes != hashSize)
{
delete[] hashBuffer; hashBuffer = nullptr;
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
return false;
}
// calculate hash
Sha256Digest calculatedHash;
sha256Calculate(hashBuffer, hashSize, calculatedHash);
delete[] hashBuffer; hashBuffer = nullptr;
//read hash data
if (headSize != SetFilePointer(m_hPakFile, headSize, 0, FILE_BEGIN))
{
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
return false;
}
Sha256Digest readedHashData;
if (!ReadFile(m_hPakFile, readedHashData.data(), readedHashData.max_size(), &dwReadBytes, 0) ||
dwReadBytes != readedHashData.max_size())
{
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
return false;
}
if (calculatedHash != readedHashData)
{
setLastError(NPK_ERR_INVALID_FILE_HASH);
return false;
}
return true;
}
void CNPKFile::buildHashFileList()
{
if (m_hPakFile == NULL)
{
setLastError(NPK_ERR_INVALID_STATE, "NPK file is not opened");
return;
}
m_hashFileList.clear();
for (int32_t i = 0; i < m_nFileCounts; i++)
{
const FileNode& fileNode = m_fileList[i];
m_hashFileList[fileNode.fileName] = i;
}
}
bool CNPKFile::closePakFile(void)
{
//Close handle
if (m_hPakFile)
{
CloseHandle(m_hPakFile); m_hPakFile = 0;
}
m_fileList.clear();
m_nFileCounts = 0;
return true;
}
bool CNPKFile::getFileNode(int32_t index, INPKFile::FileNode& fileNode) const
{
if (index < 0 || index >= getFileCount())
{
return false;
}
fileNode = m_fileList[index];
return true;
}
IFileStream* CNPKFile::openFileStream(const char* szFileName)
{
assert(szFileName && m_hPakFile);
if (!szFileName || szFileName[0] == 0)
{
setLastError(NPK_ERR_PARAM);
return nullptr;
}
if(m_hPakFile == NULL)
{
setLastError(NPK_ERR_INVALID_STATE, "NPK file is not opened");
return nullptr;
}
int32_t fileIndex = -1;
auto it = m_hashFileList.find(szFileName);
if(it==m_hashFileList.end())
{
setLastError(NPK_ERR_FILE_NOT_FOUND, "File=%s not found in NPK file", szFileName);
return nullptr;
}
else
{
fileIndex = it->second;
}
if (fileIndex < 0 || fileIndex >= getFileCount())
{
setLastError(NPK_ERR_FILE_NOT_FOUND, "File=%s, FileIndex=%d, not found in NPK file", szFileName, fileIndex);
return nullptr;
}
FileNode& fileNode = m_fileList[fileIndex];
CFileStream* pFileStream = new CFileStream(this, m_hPakFile, fileNode);
return pFileStream;
}
void CNPKFile::closeFileStream(IFileStream* fileStream)
{
if (fileStream)
{
delete ((CFileStream*)fileStream);
}
}
INPKFile* createNPKFile()
{
return new CNPKFile();
}
void closeNPKFile(INPKFile* npkFile)
{
if (npkFile)
{
delete ((CNPKFile*)npkFile);
npkFile = nullptr;
}
}
}
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#include "libNPK.h"
namespace NPK
{
class CNPKFile : public INPKFile
{
public:
//"NeoplePack_Bill\0"
static const char* kNPKFileHeaderMagic;
static const int32_t kNPKFileHeaderMagicSize = 16;
//"puchikon@neople dungeon and fighter DNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDN..."
static const char* kNPKFileNameXorCode;
static const int32_t kNPKFileNameXorCodeSize = 256;
typedef struct {
char szFileHeadMagic[kNPKFileHeaderMagicSize];
int32_t nFileCounts;
} NPKFileHeader;
typedef struct {
uint32_t fileOffset;
uint32_t fileSize;
char szFileName[256];
} FileInfo;
public:
virtual bool openPakFile(const char* szPackFileName) override;
virtual bool closePakFile(void) override;
virtual int32_t getFileCount(void) const override {
return m_nFileCounts;
}
virtual bool getFileNode(int32_t index, FileNode& fileNode) const override;
virtual IFileStream* openFileStream(const char* szFileName) override;
virtual void closeFileStream(IFileStream* fileStream) override;
private:
bool checkPakFileHash(void);
void buildHashFileList(void);
private:
std::string m_strPackFileName;
HANDLE m_hPakFile = NULL;
int32_t m_nFileCounts = 0;
typedef std::vector<FileNode> FileList;
FileList m_fileList;
typedef std::unordered_map<std::string, int32_t> FileHashList;
FileHashList m_hashFileList;
public:
CNPKFile();
virtual ~CNPKFile();
};
}
+13
View File
@@ -0,0 +1,13 @@
#include <Windows.h>
#include <Shlwapi.h>
#include <strsafe.h>
#include <pathcch.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <vector>
#include <string>
#include <algorithm>
#include <unordered_map>
+245
View File
@@ -0,0 +1,245 @@
#include "libNPK_StdAfx.h"
#include "libNPK_Utils.h"
std::string sizeToString(int64_t s)
{
char temp[64] = { 0 };
static const int64_t KB = 1024;
static const int64_t MB = 1024 * 1024;
static const int64_t GB = 1024 * 1024 * 1024;
if (s < KB) {
std::snprintf(temp, 64, "%zu ", s);
}
else if (s < MB) {
std::snprintf(temp, 64, "%.2f KB", (float)s / (float)(KB));
}
else if (s < GB) {
std::snprintf(temp, 64, "%.2f MB", (float)s / (float)(MB));
}
else {
std::snprintf(temp, 64, "%.2f GB", (float)s / (float)(GB));
}
return std::string(temp);
}
int64_t sizeFromString(const char* sizeStr)
{
if (!sizeStr || !*sizeStr) {
return 0;
}
char* endPtr = nullptr;
int64_t size = strtoull(sizeStr, &endPtr, 10);
if (endPtr && *endPtr) {
switch (*endPtr) {
case 'k':
case 'K':
size *= 1024;
break;
case 'm':
case 'M':
size *= 1024 * 1024;
break;
case 'g':
case 'G':
size *= 1024 * 1024 * 1024;
break;
}
}
return size;
}
int compareTwoFileName(const char* szFileName1, const char* szFileName2)
{
char szTempFileName1[MAX_PATH] = { 0 };
char szTempFileName2[MAX_PATH] = { 0 };
StringCchPrintfA(szTempFileName1, MAX_PATH, "%s", szFileName1);
StringCchPrintfA(szTempFileName2, MAX_PATH, "%s", szFileName2);
PathRemoveBlanksA(szTempFileName1);
PathRemoveBlanksA(szTempFileName2);
PathRemoveBackslashA(szTempFileName1);
PathRemoveBackslashA(szTempFileName2);
int nRet = _stricmp(szTempFileName1, szTempFileName2);
if (nRet == 0) return 0;
char szShortFileName1[MAX_PATH] = { 0 };
char szShortFileName2[MAX_PATH] = { 0 };
if (GetShortPathNameA(szTempFileName1, szShortFileName1, MAX_PATH))
{
StringCchCopyA(szTempFileName1, MAX_PATH, szShortFileName1);
}
if (GetShortPathNameA(szTempFileName2, szShortFileName2, MAX_PATH))
{
StringCchCopyA(szTempFileName2, MAX_PATH, szShortFileName2);
}
return _stricmp(szTempFileName1, szTempFileName2);
}
// Convert a standard ANSI string to a wide string (UTF-16)
std::wstring convertAnsiStringToWide(const char* szSource)
{
const static int DEFAULT_WCHAR_BUF_SIZE = 1024;
wchar_t DEFAULT_WCHAR_BUF[DEFAULT_WCHAR_BUF_SIZE] = { 0 };
if (szSource == 0 || szSource[0] == 0) return std::wstring(L"");
int32_t sourceLen = (int32_t)strlen(szSource) + 1;
int32_t targetLen = sourceLen + 32;
wchar_t* wszTemp = 0;
if (targetLen >= DEFAULT_WCHAR_BUF_SIZE)
wszTemp = new wchar_t[targetLen];
else
wszTemp = DEFAULT_WCHAR_BUF;
::MultiByteToWideChar(CP_ACP, 0, szSource, sourceLen, wszTemp, targetLen);
std::wstring strReturn(wszTemp);
if (wszTemp != DEFAULT_WCHAR_BUF) delete[] wszTemp;
return strReturn;
}
uint8_t* readFileToBuffer(const char* filename, int64_t& outSize)
{
FILE* file = nullptr;
fopen_s(&file, filename, "rb");
if (!file)
{
printf("Error: Could not open file %s\n", filename);
return nullptr; // Failed to open file
}
fseek(file, 0, SEEK_END);
outSize = (int64_t)ftell(file);
fseek(file, 0, SEEK_SET);
uint8_t* buffer = new uint8_t[outSize + 1];
if (outSize != fread(buffer, 1, outSize, file))
{
printf("Error: Failed to read file %s\n", filename);
delete[] buffer;
fclose(file);
return nullptr; // Failed to read file
}
fclose(file);
buffer[outSize] = '\0'; // Null-terminate the buffer
return buffer;
}
void freeFileBuffer(uint8_t* buffer)
{
if (buffer == nullptr) return;
delete[] buffer;
}
bool clearPath(const char* szPath, bool recursion, bool removeSelf)
{
char szTemp[MAX_PATH] = { 0 };
StringCbCopyA(szTemp, sizeof(szTemp)-1, szPath);
PathAppend(szTemp, "*.*");
SHFILEOPSTRUCT shf;
SecureZeroMemory(&shf, sizeof(SHFILEOPSTRUCT));
shf.hwnd = NULL;
shf.pFrom = szTemp;
shf.wFunc = FO_DELETE;
shf.fFlags = FOF_NOCONFIRMMKDIR | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
if (!recursion)
{
shf.fFlags |= FOF_NORECURSION;
}
if (0 != SHFileOperation(&shf))
{
return false; // Failed to delete files
}
if (removeSelf)
{
if (!RemoveDirectory(szPath))
{
printf("Error: Failed to remove directory %s\n", szPath);
return false; // Failed to remove directory
}
}
return true; // Successfully cleared the path
}
bool createEmptyPath(const char* szPath)
{
if (!szPath || szPath[0] == 0)
{
printf("Error: Invalid path provided for directory creation.\n");
return false; // Invalid path
}
if (::PathFileExists(szPath))
{
if (::PathIsDirectory(szPath))
{
// Clear the directory
if (!clearPath(szPath, true, false))
{
printf("Failed to clear output directory: '%s'\n", szPath);
return false; // Failed to clear output directory
}
return true; // Directory exists and is cleared
}
else
{
// If it exists but is not a directory, delete the file
if(!DeleteFileA(szPath) && ::GetLastError() != ERROR_FILE_NOT_FOUND)
{
printf("Failed to delete existing file: '%s', Error=%d\n", szPath, ::GetLastError());
return false; // Failed to delete existing file
}
}
}
if (!::CreateDirectory(szPath, NULL) && ::GetLastError() != ERROR_ALREADY_EXISTS)
{
printf("Failed to create output directory: '%s', Error=%d\n", szPath, ::GetLastError());
return false; // Failed to create output directory
}
return true;
}
void getParentPath(char* szPath)
{
if (!szPath || szPath[0] == 0) return;
size_t length = strlen(szPath);
std::replace(szPath, szPath+length, '/', '\\');
::PathRemoveFileSpecA(szPath);
}
bool forceCreatePath(const char* szPath)
{
char szCurrentPath[MAX_PATH] = { 0 };
StringCchCopyA(szCurrentPath, MAX_PATH, szPath);
size_t length = strlen(szPath);
std::replace(szCurrentPath, szCurrentPath + length, '/', '\\');
//目录已经存在
if (::PathFileExists(szCurrentPath)) return true;
//能够直接创建
if (::CreateDirectory(szCurrentPath, 0)) return true;
//取得上一级目录
char szParentPath[MAX_PATH] = { 0 };
StringCchCopyA(szParentPath, MAX_PATH, szPath);
getParentPath(szParentPath);
//创建上一级目录
if (!forceCreatePath(szParentPath)) return false;
return TRUE == ::CreateDirectory(szCurrentPath, 0);
}