in XWBTool/xwbtool.cpp [1202:1926]
int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
{
// Parameters and defaults
wchar_t szOutputFile[MAX_PATH] = {};
wchar_t szHeaderFile[MAX_PATH] = {};
// Set locale for output since GetErrorDesc can get localized strings.
std::locale::global(std::locale(""));
// Process command line
uint32_t dwOptions = 0;
std::list<SConversion> conversion;
for (int iArg = 1; iArg < argc; iArg++)
{
PWSTR pArg = argv[iArg];
if (('-' == pArg[0]) || ('/' == pArg[0]))
{
pArg++;
PWSTR pValue;
for (pValue = pArg; *pValue && (':' != *pValue); pValue++);
if (*pValue)
*pValue++ = 0;
uint32_t dwOption = LookupByName(pArg, g_pOptions);
if (!dwOption || (dwOptions & (1 << dwOption)))
{
PrintUsage();
return 1;
}
dwOptions |= 1 << dwOption;
// Handle options with additional value parameter
switch (dwOption)
{
case OPT_OUTPUTFILE:
case OPT_OUTPUTHEADER:
case OPT_FILELIST:
if (!*pValue)
{
if ((iArg + 1 >= argc))
{
PrintUsage();
return 1;
}
iArg++;
pValue = argv[iArg];
}
break;
}
switch (dwOption)
{
case OPT_OUTPUTFILE:
wcscpy_s(szOutputFile, MAX_PATH, pValue);
break;
case OPT_OUTPUTHEADER:
wcscpy_s(szHeaderFile, MAX_PATH, pValue);
break;
case OPT_ADVANCED_FORMAT:
// Must disable compact version to support 4K
if (dwOptions & (1 << OPT_COMPACT))
{
wprintf(L"-c and -af are mutually exclusive options\n");
return 1;
}
dwOptions |= (1 << OPT_NOCOMPACT);
break;
case OPT_COMPACT:
if (dwOptions & (1 << OPT_ADVANCED_FORMAT))
{
wprintf(L"-c and -af are mutually exclusive options\n");
return 1;
}
if (dwOptions & (1 << OPT_NOCOMPACT))
{
wprintf(L"-c and -nc are mutually exclusive options\n");
return 1;
}
break;
case OPT_NOCOMPACT:
if (dwOptions & (1 << OPT_COMPACT))
{
wprintf(L"-c and -nc are mutually exclusive options\n");
return 1;
}
break;
case OPT_FILELIST:
{
std::wifstream inFile(pValue);
if (!inFile)
{
wprintf(L"Error opening -flist file %ls\n", pValue);
return 1;
}
inFile.imbue(std::locale::classic());
ProcessFileList(inFile, conversion);
}
break;
}
}
else if (wcspbrk(pArg, L"?*") != nullptr)
{
size_t count = conversion.size();
SearchForFiles(pArg, conversion, (dwOptions & (1 << OPT_RECURSIVE)) != 0);
if (conversion.size() <= count)
{
wprintf(L"No matching files found for %ls\n", pArg);
return 1;
}
}
else
{
SConversion conv = {};
wcscpy_s(conv.szSrc, MAX_PATH, pArg);
conversion.push_back(conv);
}
}
if (conversion.empty())
{
wprintf(L"ERROR: Need at least 1 wave file to build wave bank\n\n");
PrintUsage();
return 0;
}
if (~dwOptions & (1 << OPT_NOLOGO))
PrintLogo();
// Determine output file name
if (!*szOutputFile)
{
auto pConv = conversion.begin();
wchar_t ext[_MAX_EXT] = {};
wchar_t fname[_MAX_FNAME] = {};
_wsplitpath_s(pConv->szSrc, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT);
if (_wcsicmp(ext, L".xwb") == 0)
{
wprintf(L"ERROR: Need to specify output file via -o\n");
return 1;
}
_wmakepath_s(szOutputFile, nullptr, nullptr, fname, L".xwb");
}
if (dwOptions & (1 << OPT_TOLOWER))
{
std::ignore = _wcslwr_s(szOutputFile);
if (*szHeaderFile)
{
std::ignore = _wcslwr_s(szHeaderFile);
}
}
if (~dwOptions & (1 << OPT_OVERWRITE))
{
if (GetFileAttributesW(szOutputFile) != INVALID_FILE_ATTRIBUTES)
{
wprintf(L"ERROR: Output file %ls already exists, use -y to overwrite!\n", szOutputFile);
return 1;
}
if (*szHeaderFile)
{
if (GetFileAttributesW(szHeaderFile) != INVALID_FILE_ATTRIBUTES)
{
wprintf(L"ERROR: Output header file %ls already exists!\n", szHeaderFile);
return 1;
}
}
}
// Gather wave files
std::unique_ptr<uint8_t[]> entries;
std::unique_ptr<char[]> entryNames;
std::vector<WaveFile> waves;
MINIWAVEFORMAT compactFormat = {};
bool xma = false;
size_t index = 0;
for (auto pConv = conversion.begin(); pConv != conversion.end(); ++pConv, ++index)
{
wchar_t ext[_MAX_EXT] = {};
wchar_t fname[_MAX_FNAME] = {};
_wsplitpath_s(pConv->szSrc, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT);
// Load source image
if (pConv != conversion.begin())
wprintf(L"\n");
wprintf(L"reading %ls", pConv->szSrc);
fflush(stdout);
WaveFile wave;
wave.conv = index;
std::unique_ptr<uint8_t[]> waveData;
HRESULT hr = DirectX::LoadWAVAudioFromFileEx(pConv->szSrc, waveData, wave.data);
if (FAILED(hr))
{
wprintf(L"\nERROR: Failed to load file (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
wave.waveData = std::move(waveData);
PrintInfo(wave);
if (wave.data.wfx->wFormatTag == WAVE_FORMAT_XMA2)
xma = true;
waves.emplace_back(std::move(wave));
}
wprintf(L"\n");
DWORD dwAlignment = ALIGNMENT_MIN;
if (dwOptions & (1 << OPT_STREAMING))
{
dwAlignment = (dwOptions & (1 << OPT_ADVANCED_FORMAT)) ? ALIGNMENT_ADVANCED_FORMAT : ALIGNMENT_DVD;
}
else if (xma)
{
// Xbox requires 2K alignment for XMA2
dwAlignment = 2048 /* XMA_BYTES_PER_PACKET */;
}
// Convert wave format to miniformat, failing if any won't map
// Check to see if we can use the compact wave bank format
bool compact = (dwOptions & (1 << OPT_NOCOMPACT)) ? false : true;
int reason = 0;
uint64_t waveOffset = 0;
for (auto it = waves.begin(); it != waves.end(); ++it)
{
if (!ConvertToMiniFormat(it->data.wfx, it->data.seek != nullptr, it->miniFmt))
{
auto cit = conversion.cbegin();
advance(cit, it->conv);
wprintf(L"ERROR: Failed encoding %ls\n", cit->szSrc);
return 1;
}
if (it == waves.begin())
{
memcpy(&compactFormat, &it->miniFmt, sizeof(MINIWAVEFORMAT));
}
else if (memcmp(&compactFormat, &it->miniFmt, sizeof(MINIWAVEFORMAT)) != 0)
{
compact = false;
reason |= 0x1;
}
if (it->data.loopLength > 0)
{
compact = false;
reason |= 0x2;
}
DWORD alignedSize = BLOCKALIGNPAD(it->data.audioBytes, dwAlignment);
waveOffset += alignedSize;
}
if (waveOffset > UINT32_MAX)
{
wprintf(L"ERROR: Audio wave data is too large to encode into wavebank (offset %llu)", waveOffset);
return 1;
}
else if (waveOffset > (MAX_COMPACT_DATA_SEGMENT_SIZE * uint64_t(dwAlignment)))
{
compact = false;
reason |= 0x4;
}
if ((dwOptions & (1 << OPT_COMPACT)) && !compact)
{
wprintf(L"ERROR: Cannot create compact wave bank:\n");
if (reason & 0x1)
{
wprintf(L"- Mismatched formats. All formats must be identical for a compact wavebank.\n");
}
if (reason & 0x2)
{
wprintf(L"- Found loop points. Compact wavebanks do not support loop points.\n");
}
if (reason & 0x4)
{
wprintf(L"- Audio wave data is too large to encode in compact wavebank (%llu > %llu).\n", waveOffset, (uint64_t(MAX_COMPACT_DATA_SEGMENT_SIZE) * uint64_t(dwAlignment)));
}
return 1;
}
// Build entry metadata (and assign wave offset within data segment)
// Build entry friendly names if requested
entries.reset(new uint8_t[(compact ? sizeof(ENTRYCOMPACT) : sizeof(ENTRY)) * waves.size()]);
if (dwOptions & (1 << OPT_FRIENDLY_NAMES))
{
entryNames.reset(new char[waves.size() * ENTRYNAME_LENGTH]);
memset(entryNames.get(), 0, sizeof(char) * waves.size() * ENTRYNAME_LENGTH);
}
waveOffset = 0;
size_t count = 0;
size_t seekEntries = 0;
for (auto it = waves.begin(); it != waves.end(); ++it, ++count)
{
DWORD alignedSize = BLOCKALIGNPAD(it->data.audioBytes, dwAlignment);
auto wfx = it->data.wfx;
uint64_t duration = 0;
switch (it->miniFmt.wFormatTag)
{
case MINIWAVEFORMAT::TAG_XMA:
if (it->data.seekCount > 0)
seekEntries += size_t(it->data.seekCount) + 1u;
duration = reinterpret_cast<const XMA2WAVEFORMATEX*>(wfx)->SamplesEncoded;
break;
case MINIWAVEFORMAT::TAG_ADPCM:
{
auto adpcmFmt = reinterpret_cast<const ADPCMEWAVEFORMAT*>(wfx);
duration = (uint64_t(it->data.audioBytes) / uint64_t(wfx->nBlockAlign)) * uint64_t(adpcmFmt->wSamplesPerBlock);
int partial = it->data.audioBytes % wfx->nBlockAlign;
if (partial)
{
if (partial >= (7 * wfx->nChannels))
duration += (uint64_t(partial) * 2 / uint64_t(wfx->nChannels - 12));
}
}
break;
case MINIWAVEFORMAT::TAG_WMA:
if (it->data.seekCount > 0)
{
seekEntries += size_t(it->data.seekCount) + 1u;
duration = it->data.seek[it->data.seekCount - 1] / uint32_t(2 * wfx->nChannels);
}
break;
default: // MINIWAVEFORMAT::TAG_PCM
duration = (uint64_t(it->data.audioBytes) * 8) / (uint64_t(wfx->wBitsPerSample) * uint64_t(wfx->nChannels));
break;
}
if (compact)
{
auto entry = reinterpret_cast<ENTRYCOMPACT*>(entries.get() + count * sizeof(ENTRYCOMPACT));
memset(entry, 0, sizeof(ENTRYCOMPACT));
assert(waveOffset <= (MAX_COMPACT_DATA_SEGMENT_SIZE * uint64_t(dwAlignment)));
entry->dwOffset = uint32_t(waveOffset / dwAlignment);
assert(dwAlignment <= 2048);
entry->dwLengthDeviation = alignedSize - it->data.audioBytes;
}
else
{
auto entry = reinterpret_cast<ENTRY*>(entries.get() + count * sizeof(ENTRY));
memset(entry, 0, sizeof(ENTRY));
if (duration > 268435455)
{
wprintf(L"ERROR: Duration of audio too long to encode into wavebank (%llu > 2^28))\n", duration);
return 1;
}
entry->Duration = uint32_t(duration);
memcpy(&entry->Format, &it->miniFmt, sizeof(MINIWAVEFORMAT));
entry->PlayRegion.dwOffset = uint32_t(waveOffset);
entry->PlayRegion.dwLength = it->data.audioBytes;
if (it->data.loopLength > 0)
{
entry->LoopRegion.dwStartSample = it->data.loopStart;
entry->LoopRegion.dwTotalSamples = it->data.loopLength;
}
}
if (dwOptions & (1 << OPT_FRIENDLY_NAMES))
{
auto cit = conversion.cbegin();
advance(cit, it->conv);
wchar_t wEntryName[_MAX_FNAME] = {};
_wsplitpath_s(cit->szSrc, nullptr, 0, nullptr, 0, wEntryName, _MAX_FNAME, nullptr, 0);
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS, wEntryName, -1, &entryNames[count * ENTRYNAME_LENGTH], ENTRYNAME_LENGTH, nullptr, nullptr);
if (result <= 0)
{
memset(&entryNames[count * ENTRYNAME_LENGTH], 0, ENTRYNAME_LENGTH);
}
}
waveOffset += alignedSize;
}
assert(count > 0 && count == waves.size());
// Create wave bank
assert(*szOutputFile != 0);
wprintf(L"writing %ls%ls wavebank %ls w/ %zu entries\n", (compact) ? L"compact " : L"", (dwOptions & (1 << OPT_STREAMING)) ? L"streaming" : L"in-memory", szOutputFile, waves.size());
fflush(stdout);
ScopedHandle hFile(safe_handle(CreateFileW(szOutputFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)));
if (!hFile)
{
wprintf(L"ERROR: Failed opening output file %ls, %lu\n", szOutputFile, GetLastError());
return 1;
}
// Setup wave bank header
HEADER header = {};
header.dwSignature = HEADER::SIGNATURE;
header.dwHeaderVersion = HEADER::VERSION;
header.dwVersion = XACT_CONTENT_VERSION;
DWORD segmentOffset = sizeof(HEADER);
// Write bank metadata
assert((segmentOffset % 4) == 0);
BANKDATA data = {};
data.dwEntryCount = uint32_t(waves.size());
data.dwAlignment = dwAlignment;
GetSystemTimeAsFileTime(&data.BuildTime);
data.dwFlags = (dwOptions & (1 << OPT_STREAMING)) ? BANKDATA::TYPE_STREAMING : BANKDATA::TYPE_BUFFER;
if (seekEntries > 0)
{
data.dwFlags |= BANKDATA::FLAGS_SEEKTABLES;
}
if (dwOptions & (1 << OPT_FRIENDLY_NAMES))
{
data.dwFlags |= BANKDATA::FLAGS_ENTRYNAMES;
data.dwEntryNameElementSize = ENTRYNAME_LENGTH;
}
if (compact)
{
data.dwFlags |= BANKDATA::FLAGS_COMPACT;
data.dwEntryMetaDataElementSize = sizeof(ENTRYCOMPACT);
memcpy(&data.CompactFormat, &compactFormat, sizeof(MINIWAVEFORMAT));
}
else
{
data.dwEntryMetaDataElementSize = sizeof(ENTRY);
}
{
wchar_t wBankName[_MAX_FNAME] = {};
_wsplitpath_s(szOutputFile, nullptr, 0, nullptr, 0, wBankName, _MAX_FNAME, nullptr, 0);
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS, wBankName, -1, data.szBankName, BANKDATA::BANKNAME_LENGTH, nullptr, nullptr);
if (result <= 0)
{
memset(data.szBankName, 0, BANKDATA::BANKNAME_LENGTH);
}
}
if (SetFilePointer(hFile.get(), LONG(segmentOffset), nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
wprintf(L"ERROR: Failed writing bank data to %ls, SFP %lu\n", szOutputFile, GetLastError());
return 1;
}
DWORD bytesWritten;
if (!WriteFile(hFile.get(), &data, sizeof(data), &bytesWritten, nullptr)
|| bytesWritten != sizeof(data))
{
wprintf(L"ERROR: Failed writing bank data to %ls, %lu\n", szOutputFile, GetLastError());
return 1;
}
header.Segments[HEADER::SEGIDX_BANKDATA].dwOffset = segmentOffset;
header.Segments[HEADER::SEGIDX_BANKDATA].dwLength = sizeof(BANKDATA);
segmentOffset += sizeof(BANKDATA);
// Write entry metadata
assert((segmentOffset % 4) == 0);
if (SetFilePointer(hFile.get(), LONG(segmentOffset), nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
wprintf(L"ERROR: Failed writing entry metadata to %ls, SFP %lu\n", szOutputFile, GetLastError());
return 1;
}
uint32_t entryBytes = uint32_t(waves.size() * data.dwEntryMetaDataElementSize);
if (!WriteFile(hFile.get(), entries.get(), entryBytes, &bytesWritten, nullptr)
|| bytesWritten != entryBytes)
{
wprintf(L"ERROR: Failed writing entry metadata to %ls, %lu\n", szOutputFile, GetLastError());
return 1;
}
header.Segments[HEADER::SEGIDX_ENTRYMETADATA].dwOffset = segmentOffset;
header.Segments[HEADER::SEGIDX_ENTRYMETADATA].dwLength = entryBytes;
segmentOffset += entryBytes;
// Write seek tables
assert((segmentOffset % 4) == 0);
header.Segments[HEADER::SEGIDX_SEEKTABLES].dwOffset = segmentOffset;
if (seekEntries > 0)
{
seekEntries += waves.size(); // Room for an offset per entry
auto seekTables = std::make_unique<uint32_t[]>(seekEntries);
if (SetFilePointer(hFile.get(), LONG(segmentOffset), nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
wprintf(L"ERROR: Failed writing seek tables to %ls, SFP %lu\n", szOutputFile, GetLastError());
return 1;
}
uint32_t seekoffset = 0;
uint32_t windex = 0;
for (auto it = waves.begin(); it != waves.end(); ++it, ++windex)
{
if (it->miniFmt.wFormatTag == MINIWAVEFORMAT::TAG_WMA)
{
seekTables[windex] = seekoffset * sizeof(uint32_t);
uint32_t baseoffset = uint32_t(waves.size() + seekoffset);
seekTables[baseoffset] = it->data.seekCount;
for (uint32_t j = 0; j < it->data.seekCount; ++j)
{
seekTables[size_t(baseoffset) + size_t(j) + 1u] = it->data.seek[j];
}
seekoffset += size_t(it->data.seekCount) + 1u;
}
else if (it->miniFmt.wFormatTag == MINIWAVEFORMAT::TAG_XMA)
{
seekTables[windex] = seekoffset * sizeof(uint32_t);
uint32_t baseoffset = uint32_t(waves.size() + seekoffset);
seekTables[baseoffset] = it->data.seekCount;
for (uint32_t j = 0; j < it->data.seekCount; ++j)
{
seekTables[size_t(baseoffset) + size_t(j) + 1u] = _byteswap_ulong(it->data.seek[j]);
}
seekoffset += it->data.seekCount + 1;
}
else
{
seekTables[windex] = uint32_t(-1);
}
}
uint32_t seekLen = uint32_t(sizeof(uint32_t) * seekEntries);
if (!WriteFile(hFile.get(), seekTables.get(), seekLen, &bytesWritten, nullptr)
|| bytesWritten != seekLen)
{
wprintf(L"ERROR: Failed writing seek tables to %ls, %lu\n", szOutputFile, GetLastError());
return 1;
}
segmentOffset += seekLen;
header.Segments[HEADER::SEGIDX_SEEKTABLES].dwLength = seekLen;
}
else
{
header.Segments[HEADER::SEGIDX_SEEKTABLES].dwLength = 0;
}
// Write entry names
if (dwOptions & (1 << OPT_FRIENDLY_NAMES))
{
assert((segmentOffset % 4) == 0);
if (SetFilePointer(hFile.get(), LONG(segmentOffset), nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
wprintf(L"ERROR: Failed writing friendly entry names to %ls, SFP %lu\n", szOutputFile, GetLastError());
return 1;
}
uint32_t entryNamesBytes = uint32_t(count * data.dwEntryNameElementSize);
if (!WriteFile(hFile.get(), entryNames.get(), entryNamesBytes, &bytesWritten, nullptr)
|| bytesWritten != entryNamesBytes)
{
wprintf(L"ERROR: Failed writing friendly entry names to %ls, %lu\n", szOutputFile, GetLastError());
return 1;
}
header.Segments[HEADER::SEGIDX_ENTRYNAMES].dwOffset = segmentOffset;
header.Segments[HEADER::SEGIDX_ENTRYNAMES].dwLength = entryNamesBytes;
segmentOffset += entryNamesBytes;
}
// Write wave data
segmentOffset = BLOCKALIGNPAD(segmentOffset, dwAlignment);
header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwOffset = segmentOffset;
header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength = uint32_t(waveOffset);
for (auto& it : waves)
{
if (SetFilePointer(hFile.get(), LONG(segmentOffset), nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
wprintf(L"ERROR: Failed writing audio data to %ls, SFP %lu\n", szOutputFile, GetLastError());
return 1;
}
if (!WriteFile(hFile.get(), it.data.startAudio, it.data.audioBytes, &bytesWritten, nullptr)
|| bytesWritten != it.data.audioBytes)
{
wprintf(L"ERROR: Failed writing audio data to %ls, %lu\n", szOutputFile, GetLastError());
return 1;
}
DWORD alignedSize = BLOCKALIGNPAD(it.data.audioBytes, dwAlignment);
if ((uint64_t(segmentOffset) + alignedSize) > UINT32_MAX)
{
wprintf(L"ERROR: Data exceeds maximum size for wavebank\n");
return 1;
}
segmentOffset += alignedSize;
}
assert(segmentOffset == (header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwOffset + waveOffset));
// Commit wave bank
if (SetFilePointer(hFile.get(), LONG(segmentOffset), nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
wprintf(L"ERROR: Failed committing output file %ls, EOF %lu\n", szOutputFile, GetLastError());
return 1;
}
if (!SetEndOfFile(hFile.get()))
{
wprintf(L"ERROR: Failed committing output file %ls, EOF %lu\n", szOutputFile, GetLastError());
return 1;
}
if (SetFilePointer(hFile.get(), 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
wprintf(L"ERROR: Failed committing output file %ls, HDR %lu\n", szOutputFile, GetLastError());
return 1;
}
if (!WriteFile(hFile.get(), &header, sizeof(header), &bytesWritten, nullptr)
|| bytesWritten != sizeof(header))
{
wprintf(L"ERROR: Failed committing output file %ls, HDR %lu\n", szOutputFile, GetLastError());
return 1;
}
// Write C header if requested
if (*szHeaderFile)
{
wprintf(L"writing C header %ls\n", szHeaderFile);
fflush(stdout);
FILE* file = nullptr;
if (!_wfopen_s(&file, szHeaderFile, L"wt"))
{
wchar_t wBankName[_MAX_FNAME] = {};
_wsplitpath_s(szOutputFile, nullptr, 0, nullptr, 0, wBankName, _MAX_FNAME, nullptr, 0);
FileNameToIdentifier(wBankName, _MAX_FNAME);
fprintf_s(file, "#pragma once\n\nenum XACT_WAVEBANK_%ls : unsigned int\n{\n", wBankName);
size_t windex = 0;
for (auto it = waves.begin(); it != waves.end(); ++it, ++windex)
{
auto cit = conversion.cbegin();
advance(cit, it->conv);
wchar_t wEntryName[_MAX_FNAME] = {};
_wsplitpath_s(cit->szSrc, nullptr, 0, nullptr, 0, wEntryName, _MAX_FNAME, nullptr, 0);
FileNameToIdentifier(wEntryName, _MAX_FNAME);
fprintf_s(file, " XACT_WAVEBANK_%ls_%ls = %zu,\n", wBankName, wEntryName, windex);
}
fprintf_s(file, "};\n\n#define XACT_WAVEBANK_%ls_ENTRY_COUNT %zu\n", wBankName, count);
fclose(file);
}
else
{
wprintf(L"ERROR: Failed writing wave bank C header %ls\n", szHeaderFile);
return 1;
}
}
return 0;
}