release tool finished
This commit is contained in:
1 parent
ae4b6acc55
commit
fd434c3f68
2 files changed
+294
-55
No files matched your search
@@ -68,14 +68,9 @@ all: $(STD_TARGET_LIST) $(EXT_TARGET_LIST)
|
|||||||
clean:
|
clean:
|
||||||
@rm -rf build
|
@rm -rf build
|
||||||
|
|
||||||
updatebin: $(STD_TARGET_LIST) $(STD_TOOL_RELEASE)
|
updatebin: $(STD_TARGET_LIST) $(TOOL_RELEASE)
|
||||||
|
$(TOOL_RELEASE) -i ./build -o ./bin basic_generic.rom kernal_generic.rom basic_mega65.rom kernal_mega65.rom basic_ultimate64.rom kernal_ultimate64.rom
|
||||||
cp build/chargen.rom bin/chargen.rom
|
cp build/chargen.rom bin/chargen.rom
|
||||||
cp build/basic_generic.rom bin/basic_generic.rom
|
|
||||||
cp build/kernal_generic.rom bin/kernal_generic.rom
|
|
||||||
cp build/basic_mega65.rom bin/basic_mega65.rom
|
|
||||||
cp build/kernal_mega65.rom bin/kernal_mega65.rom
|
|
||||||
cp build/basic_ultimate64.rom bin/basic_ultimate64.rom
|
|
||||||
cp build/kernal_ultimate64.rom bin/kernal_ultimate64.rom
|
|
||||||
|
|
||||||
# Rules - tools
|
# Rules - tools
|
||||||
|
|
||||||
|
|||||||
+292
-48
@@ -16,12 +16,45 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
std::string romTypeName;
|
||||||
|
size_t fileSize;
|
||||||
|
size_t signatureOffset; // offset to 'OR' string
|
||||||
|
bool requireIdBASIC;
|
||||||
|
bool requireIdKERNAL;
|
||||||
|
|
||||||
|
} ROMTypeDescriptionEntry;
|
||||||
|
|
||||||
|
const std::vector<ROMTypeDescriptionEntry> ROM_DEFINITIONS =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"BASIC",
|
||||||
|
8192, // file size
|
||||||
|
0x1F52, // signature offset
|
||||||
|
true, // whether to require BASIC ID
|
||||||
|
false // whether to require KERNAL ID
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"KERNAL",
|
||||||
|
8192, // file size
|
||||||
|
0x04B9, // signature offset
|
||||||
|
false, // whether to require BASIC ID
|
||||||
|
false // whether to require KERNAL ID
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint16_t MAX_FILE_SIZE = 8192;
|
||||||
|
|
||||||
const std::vector<uint8_t> STR_MEGABAS = { 0x4D, 0x45, 0x47, 0x41, 0x42, 0x41, 0x53, 0x32 };
|
const std::vector<uint8_t> STR_MEGABAS = { 0x4D, 0x45, 0x47, 0x41, 0x42, 0x41, 0x53, 0x32 };
|
||||||
const std::vector<uint8_t> STR_OR = { 0x4F, 0x52 };
|
const std::vector<uint8_t> STR_OR = { 0x4F, 0x52 };
|
||||||
const std::vector<uint8_t> STR_SNAPSHOT = { 0x28, 0x44, 0x45, 0x56, 0x45, 0x4C, 0x20, 0x53,
|
const std::vector<uint8_t> STR_SNAPSHOT = { 0x28, 0x44, 0x45, 0x56, 0x45, 0x4C, 0x20, 0x53,
|
||||||
0x4E, 0x41, 0x50, 0x53, 0x48, 0x4F, 0x54, 0x29,
|
0x4E, 0x41, 0x50, 0x53, 0x48, 0x4F, 0x54, 0x29,
|
||||||
0x00 };
|
0x00 };
|
||||||
|
|
||||||
|
const std::string STR_DEV = "DEV.";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Command and enviroment line settings
|
// Command and enviroment line settings
|
||||||
//
|
//
|
||||||
@@ -32,7 +65,7 @@ std::string CMD_outDir = "./bin";
|
|||||||
std::list<std::string> CMD_fileList;
|
std::list<std::string> CMD_fileList;
|
||||||
|
|
||||||
std::string ENV_developerId;
|
std::string ENV_developerId;
|
||||||
std::string ENV_timeString;
|
std::string ENV_dateString;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Common helper functions
|
// Common helper functions
|
||||||
@@ -49,38 +82,42 @@ void printUsage()
|
|||||||
// Class definitions
|
// Class definitions
|
||||||
//
|
//
|
||||||
|
|
||||||
enum class ROMType
|
|
||||||
{
|
|
||||||
ROM_UNKNOWN,
|
|
||||||
ROM_BASIC,
|
|
||||||
ROM_KERNAL
|
|
||||||
};
|
|
||||||
|
|
||||||
class ROMFile
|
class ROMFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ROMFile(const std::string &baseFileName);
|
ROMFile(const std::string &baseFileName);
|
||||||
|
|
||||||
|
void embedRevisionStr(const std::string &newRevisionStr);
|
||||||
|
void save();
|
||||||
|
|
||||||
|
const ROMTypeDescriptionEntry *descPtr;
|
||||||
|
|
||||||
|
bool sameContent;
|
||||||
|
bool sameDeveloper;
|
||||||
|
bool sameDate; // only set if sameDeveloper
|
||||||
|
uint8_t oldDailyRelId; // only set if sameDeveloper and sameDate
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void readSrcFile();
|
void readSrcFile();
|
||||||
void readDstFile();
|
void readDstFile();
|
||||||
|
|
||||||
void recognizeFiles();
|
void recognizeSrcFile();
|
||||||
|
void dropUnmatchingDst();
|
||||||
|
void analyzeContent();
|
||||||
|
|
||||||
std::string baseFileName;
|
std::string baseFileName;
|
||||||
|
|
||||||
std::vector<uint8_t> srcFileContent;
|
std::vector<uint8_t> srcFileContent;
|
||||||
std::vector<uint8_t> dstFileContent;
|
std::vector<uint8_t> dstFileContent;
|
||||||
|
|
||||||
ROMType romType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Global variables
|
// Global variables
|
||||||
//
|
//
|
||||||
|
|
||||||
|
size_t GLOBAL_maxFileNameLen = 0;
|
||||||
std::list<ROMFile> GLOBAL_ROMFiles;
|
std::list<ROMFile> GLOBAL_ROMFiles;
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -151,18 +188,17 @@ void getDate()
|
|||||||
|
|
||||||
const std::string zero = "0";
|
const std::string zero = "0";
|
||||||
|
|
||||||
|
ENV_dateString = (year.size() == 2 ? year : zero + year) +
|
||||||
ENV_timeString = (year.size() == 2 ? year : zero + year) +
|
|
||||||
(month.size() == 2 ? month : zero + month) +
|
(month.size() == 2 ? month : zero + month) +
|
||||||
(day.size() == 2 ? day : zero + day);
|
(day.size() == 2 ? day : zero + day);
|
||||||
|
|
||||||
if (ENV_timeString.size() != 6) ERROR("could not retrieve date");
|
if (ENV_dateString.size() != 6) ERROR("could not retrieve date");
|
||||||
}
|
}
|
||||||
|
|
||||||
void printBanner()
|
void printBanner()
|
||||||
{
|
{
|
||||||
printBannerLineTop();
|
printBannerLineTop();
|
||||||
std::cout << "// Preparing ROM set release " << ENV_timeString << "-" << ENV_developerId << "\n";
|
std::cout << "// Preparing ROM set release" << "\n";
|
||||||
printBannerLineBottom();
|
printBannerLineBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,14 +207,69 @@ void readFiles()
|
|||||||
for (auto &fileName : CMD_fileList) GLOBAL_ROMFiles.push_back(ROMFile(fileName));
|
for (auto &fileName : CMD_fileList) GLOBAL_ROMFiles.push_back(ROMFile(fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
void embedVersionString()
|
void decideIfUpdate()
|
||||||
{
|
{
|
||||||
// XXX
|
// Check if we have any file at all
|
||||||
|
|
||||||
|
if (GLOBAL_ROMFiles.empty())
|
||||||
|
{
|
||||||
|
ERROR("no files to update");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have at least one file with changed content
|
||||||
|
|
||||||
|
bool sameContent = true;
|
||||||
|
for (auto &file : GLOBAL_ROMFiles)
|
||||||
|
{
|
||||||
|
if (!file.sameContent)
|
||||||
|
{
|
||||||
|
sameContent = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sameContent)
|
||||||
|
{
|
||||||
|
ERROR("same content in all the files");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeFiles()
|
void saveFiles()
|
||||||
{
|
{
|
||||||
// XXX
|
// Determine a daily revision sequence ID
|
||||||
|
|
||||||
|
uint8_t relId = 0;
|
||||||
|
for (auto &file : GLOBAL_ROMFiles)
|
||||||
|
{
|
||||||
|
if (file.sameDeveloper && file.sameDate)
|
||||||
|
{
|
||||||
|
relId = std::max(file.oldDailyRelId, relId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
relId++;
|
||||||
|
|
||||||
|
if (relId > 9)
|
||||||
|
{
|
||||||
|
ERROR("too many releases this day");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new revision string
|
||||||
|
|
||||||
|
const std::string newRevStr = STR_DEV + ENV_dateString + "." + ENV_developerId + "." + std::to_string(relId);
|
||||||
|
|
||||||
|
for (auto &file : GLOBAL_ROMFiles)
|
||||||
|
{
|
||||||
|
file.embedRevisionStr(newRevStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Files for release '" << newRevStr << "':\n\n";
|
||||||
|
|
||||||
|
// Save the files
|
||||||
|
|
||||||
|
for (auto &file : GLOBAL_ROMFiles)
|
||||||
|
{
|
||||||
|
file.save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -194,12 +285,12 @@ int main(int argc, char **argv)
|
|||||||
printBanner();
|
printBanner();
|
||||||
|
|
||||||
readFiles();
|
readFiles();
|
||||||
embedVersionString();
|
decideIfUpdate();
|
||||||
writeFiles();
|
saveFiles();
|
||||||
|
|
||||||
std::cout << "\n\nXXX This tool is not finished yet\n\n";
|
std::cout << "\n";
|
||||||
|
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -207,13 +298,21 @@ int main(int argc, char **argv)
|
|||||||
//
|
//
|
||||||
|
|
||||||
ROMFile::ROMFile(const std::string &baseFileName) :
|
ROMFile::ROMFile(const std::string &baseFileName) :
|
||||||
baseFileName(baseFileName),
|
descPtr(nullptr),
|
||||||
romType(ROMType::ROM_UNKNOWN)
|
sameContent(false),
|
||||||
|
sameDeveloper(false),
|
||||||
|
sameDate(false),
|
||||||
|
oldDailyRelId(0),
|
||||||
|
baseFileName(baseFileName)
|
||||||
{
|
{
|
||||||
|
GLOBAL_maxFileNameLen = std::max(GLOBAL_maxFileNameLen, baseFileName.length());
|
||||||
|
|
||||||
readSrcFile();
|
readSrcFile();
|
||||||
readDstFile();
|
readDstFile();
|
||||||
|
|
||||||
recognizeFiles();
|
recognizeSrcFile();
|
||||||
|
dropUnmatchingDst();
|
||||||
|
analyzeContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ROMFile::readSrcFile()
|
void ROMFile::readSrcFile()
|
||||||
@@ -228,9 +327,9 @@ void ROMFile::readSrcFile()
|
|||||||
if (!srcFile.good()) ERROR(std::string("unable to open ROM file '") + srcFileNamePath + "'");
|
if (!srcFile.good()) ERROR(std::string("unable to open ROM file '") + srcFileNamePath + "'");
|
||||||
|
|
||||||
auto srcSize = srcFile.tellg();
|
auto srcSize = srcFile.tellg();
|
||||||
if (srcSize != 8192) ERROR(std::string("incorrect size of ROM file '") + srcFileNamePath + "'");
|
if (srcSize > MAX_FILE_SIZE) ERROR(std::string("incorrect size of ROM file '") + srcFileNamePath + "'");
|
||||||
|
|
||||||
srcFileContent.resize(8192);
|
srcFileContent.resize(srcSize);
|
||||||
srcFile.seekg (0, srcFile.beg);
|
srcFile.seekg (0, srcFile.beg);
|
||||||
srcFile.read((char *) srcFileContent.data(), srcFileContent.size());
|
srcFile.read((char *) srcFileContent.data(), srcFileContent.size());
|
||||||
|
|
||||||
@@ -249,45 +348,190 @@ void ROMFile::readDstFile()
|
|||||||
|
|
||||||
bool continueReading = dstFile.good();
|
bool continueReading = dstFile.good();
|
||||||
|
|
||||||
if (continueReading && dstFile.tellg() != 8192) continueReading = false;
|
if (continueReading && dstFile.tellg() != unsigned(srcFileContent.size())) continueReading = false;
|
||||||
|
|
||||||
if (continueReading)
|
if (continueReading)
|
||||||
{
|
{
|
||||||
dstFileContent.resize(8192);
|
dstFileContent.resize(srcFileContent.size());
|
||||||
dstFile.seekg (0, dstFile.beg);
|
dstFile.seekg (0, dstFile.beg);
|
||||||
dstFile.read((char *) dstFileContent.data(), dstFileContent.size());
|
dstFile.read((char *) dstFileContent.data(), dstFileContent.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dstFile.good()) dstFileContent.resize(0);
|
if (!dstFile.good()) dstFileContent.clear();
|
||||||
dstFile.close();
|
dstFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ROMFile::recognizeFiles()
|
void ROMFile::recognizeSrcFile()
|
||||||
{
|
{
|
||||||
// Recognize source file type
|
// Recognize source file type
|
||||||
|
|
||||||
std::cout << baseFileName << " ";
|
for (auto &SPEC : ROM_DEFINITIONS)
|
||||||
|
{
|
||||||
|
if (srcFileContent.size() != SPEC.fileSize) continue;
|
||||||
|
|
||||||
if ((memcmp(&srcFileContent[0x0004], STR_MEGABAS.data(), STR_MEGABAS.size()) == 0) &&
|
if (SPEC.requireIdBASIC && memcmp(&srcFileContent[0x0004], STR_MEGABAS.data(), STR_MEGABAS.size()) != 0)
|
||||||
(memcmp(&srcFileContent[0x1F52], STR_OR.data(), STR_OR.size()) == 0) &&
|
{
|
||||||
(memcmp(&srcFileContent[0x1F55], STR_SNAPSHOT.data(), STR_SNAPSHOT.size()) == 0))
|
continue;
|
||||||
{
|
}
|
||||||
romType = ROMType::ROM_BASIC;
|
|
||||||
|
if (SPEC.requireIdKERNAL && srcFileContent[0x1F80] != 0xF0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(&srcFileContent[SPEC.signatureOffset], STR_OR.data(), STR_OR.size()) != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto snapshotStrOffset = SPEC.signatureOffset + STR_OR.size() + 1;
|
||||||
|
if (memcmp(&srcFileContent[snapshotStrOffset], STR_SNAPSHOT.data(), STR_SNAPSHOT.size()) != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto zeroByteoffset = snapshotStrOffset + 0x10;
|
||||||
|
if (srcFileContent[zeroByteoffset] != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
descPtr = &SPEC;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (srcFileContent[0x1F80] == 0xF0 &&
|
|
||||||
(memcmp(&srcFileContent[0x04B9], STR_OR.data(), STR_OR.size()) == 0) &&
|
if (descPtr == nullptr)
|
||||||
(memcmp(&srcFileContent[0x04BC], STR_SNAPSHOT.data(), STR_SNAPSHOT.size()) == 0))
|
|
||||||
{
|
|
||||||
romType = ROMType::ROM_KERNAL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
ERROR(std::string("'") + CMD_inDir + DIR_SEPARATOR + baseFileName +
|
ERROR(std::string("'") + CMD_inDir + DIR_SEPARATOR + baseFileName +
|
||||||
"' is not a supported unversioned Open ROMs image");
|
"' is not a supported unversioned Open ROMs image");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// XXX
|
void ROMFile::dropUnmatchingDst()
|
||||||
|
{
|
||||||
|
// Drop unmatching destination file data
|
||||||
|
|
||||||
std::cout << baseFileName << " ";
|
if (!dstFileContent.empty() && descPtr->requireIdBASIC &&
|
||||||
|
memcmp(&dstFileContent[0x0004], STR_MEGABAS.data(), STR_MEGABAS.size()) != 0)
|
||||||
|
{
|
||||||
|
dstFileContent.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dstFileContent.empty() && descPtr->requireIdKERNAL && dstFileContent[0x1F80] != 0xF0)
|
||||||
|
{
|
||||||
|
dstFileContent.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dstFileContent.empty() &&
|
||||||
|
memcmp(&dstFileContent[descPtr->signatureOffset], STR_OR.data(), STR_OR.size()) != 0)
|
||||||
|
{
|
||||||
|
dstFileContent.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ROMFile::analyzeContent()
|
||||||
|
{
|
||||||
|
auto revOffset = descPtr->signatureOffset + 3;
|
||||||
|
|
||||||
|
if (dstFileContent.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if meaningful file content is really different
|
||||||
|
|
||||||
|
if ((memcmp(&srcFileContent[0x0000], &dstFileContent[0x0000], revOffset) == 0) &&
|
||||||
|
(memcmp(&srcFileContent[revOffset + 0x10], &dstFileContent[revOffset + 0x10], 8192 - revOffset) == 0))
|
||||||
|
{
|
||||||
|
sameContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine expected length of developer ID
|
||||||
|
|
||||||
|
uint8_t devIdLen = ENV_developerId.length();
|
||||||
|
|
||||||
|
// Determine if old version is a devel version compatible with this tool
|
||||||
|
|
||||||
|
if (memcmp(&dstFileContent[revOffset], STR_DEV.data(), STR_DEV.size()) != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t idxDate = revOffset + STR_DEV.size();
|
||||||
|
uint16_t idxLimit = idxDate + ENV_dateString.length();
|
||||||
|
|
||||||
|
uint16_t idx = idxDate;
|
||||||
|
for (; idx < idxLimit; idx++)
|
||||||
|
{
|
||||||
|
if (dstFileContent[idx] < '0' || dstFileContent[idx] > '9')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dstFileContent[idx] != '.' ||
|
||||||
|
dstFileContent[idx + devIdLen + 1] != '.' ||
|
||||||
|
dstFileContent[idx + devIdLen + 2] < '0' ||
|
||||||
|
dstFileContent[idx + devIdLen + 2] > '9' ||
|
||||||
|
dstFileContent[idx + devIdLen + 3] != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve release data
|
||||||
|
|
||||||
|
if (memcmp(&dstFileContent[idx + 1], ENV_developerId.data(), ENV_developerId.size()) != 0)
|
||||||
|
{
|
||||||
|
return; // different developer
|
||||||
|
}
|
||||||
|
|
||||||
|
sameDeveloper = true;
|
||||||
|
|
||||||
|
if (memcmp(&dstFileContent[idxDate], ENV_dateString.data(), ENV_dateString.size()) != 0)
|
||||||
|
{
|
||||||
|
return; // different date
|
||||||
|
}
|
||||||
|
|
||||||
|
sameDate = true;
|
||||||
|
oldDailyRelId = dstFileContent[idx + devIdLen + 2] - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void ROMFile::embedRevisionStr(const std::string &newRevisionStr)
|
||||||
|
{
|
||||||
|
std::string revStr = newRevisionStr;
|
||||||
|
|
||||||
|
while (revStr.length() < 16) revStr = revStr + '\0';
|
||||||
|
|
||||||
|
memcpy(&srcFileContent[descPtr->signatureOffset + 3],
|
||||||
|
revStr.data(), revStr.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ROMFile::save()
|
||||||
|
{
|
||||||
|
const std::string outFileNamePath = CMD_outDir + DIR_SEPARATOR + baseFileName;
|
||||||
|
|
||||||
|
// Print out file name and metadata
|
||||||
|
|
||||||
|
std::string spacing;
|
||||||
|
spacing.resize(GLOBAL_maxFileNameLen + 4 - baseFileName.length(), ' ');
|
||||||
|
|
||||||
|
std::cout << " " << outFileNamePath << spacing << descPtr->romTypeName;
|
||||||
|
|
||||||
|
if (sameContent)
|
||||||
|
{
|
||||||
|
spacing.resize(10 - descPtr->romTypeName.length());
|
||||||
|
std::cout << spacing << "(unchanged)";
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "\n";
|
std::cout << "\n";
|
||||||
|
|
||||||
|
std::ofstream outFile;
|
||||||
|
outFile.open(outFileNamePath, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
|
|
||||||
|
if (!outFile.good()) ERROR(std::string("unable to open output file '") + outFileNamePath + "'");
|
||||||
|
|
||||||
|
outFile.write((char *) srcFileContent.data(), srcFileContent.size());
|
||||||
|
|
||||||
|
if (!outFile.good()) ERROR(std::string("unable to write file '") + outFileNamePath + "'");
|
||||||
|
outFile.close();
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user