168 lines
7.8 KiB
C++
168 lines
7.8 KiB
C++
#include "EterBase/StdAfx.h"
|
|
#include "EterBase/Utils.h"
|
|
#include "EterBase/FileLoader.h"
|
|
#include "EterBase/CRC32.h"
|
|
#include "EterBase/MappedFile.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unistd.h>
|
|
|
|
int main()
|
|
{
|
|
int failures = 0;
|
|
auto check = [&failures](bool ok, const char *name) {
|
|
if (!ok) {
|
|
std::fprintf(stderr, "FAIL %s\n", name);
|
|
++failures;
|
|
}
|
|
};
|
|
check(MIN(-3, 8) == -3 && MIN(8, -3) == -3, "MIN order/sign");
|
|
check(MAX(-3, 8) == 8 && MAX(8, -3) == 8, "MAX order/sign");
|
|
check(MINMAX(2, -5, 9) == 2, "MINMAX lower");
|
|
check(MINMAX(2, 20, 9) == 9, "MINMAX upper");
|
|
check(MINMAX(9, 4, 2) == 9, "MINMAX reversed bounds");
|
|
check(fMIN(-3.5f, 8.5f) == -3.5f, "fMIN sign");
|
|
check(fMAX(200.0f, 0.0f) == 200.0f, "fMAX camera floor");
|
|
check(fMINMAX(2.0f, -5.0f, 9.0f) == 2.0f, "fMINMAX lower");
|
|
check(fMINMAX(2.0f, 20.0f, 9.0f) == 9.0f, "fMINMAX upper");
|
|
|
|
// Path helpers (40250 bodies): CFlyingData::LoadScriptFile resolves a relative attach .mse against
|
|
// GetOnlyPathName of the .msf unless IsGlobalFileName (a drive letter) says it is already absolute.
|
|
check(std::string(GetOnlyPathName("d:/ymir work/effect/a.msf")) == "d:/ymir work/effect/", "GetOnlyPathName dir");
|
|
check(std::string(GetOnlyPathName("a.msf")).empty(), "GetOnlyPathName bare");
|
|
check(IsGlobalFileName("d:/ymir work/a.mse") && !IsGlobalFileName("etc/a.mse"), "IsGlobalFileName");
|
|
std::string name;
|
|
GetOnlyFileName("d:\\ymir work\\a.mse", name);
|
|
check(name == "a.mse", "GetOnlyFileName backslash");
|
|
std::string path, stem, ext;
|
|
GetFilePathNameExtension("d:/x/y.tar.gz", (int) strlen("d:/x/y.tar.gz"), &path, &stem, &ext);
|
|
check(path == "d:/x/" && stem == "y.tar" && ext == "gz", "GetFilePathNameExtension");
|
|
ext.clear();
|
|
GetFileExtension("a/b.mse", 7, &ext);
|
|
check(ext == "mse", "GetFileExtension");
|
|
char p[64] = {}, n[64] = {}, e[64] = {};
|
|
GetFileNameParts("d:/x/y.dds", 10, p, n, e);
|
|
check(!strcmp(p, "d:/x/") && !strcmp(n, "y") && !strcmp(e, "dds"), "GetFileNameParts");
|
|
std::vector<std::string> tokens;
|
|
check(SplitLine("a \"b c\" d", " ", &tokens) && tokens.size() == 3 && tokens[1] == "b c", "SplitLine quotes");
|
|
std::string except = "a-b-c";
|
|
StringExceptCharacter(&except, "-");
|
|
check(except == "abc", "StringExceptCharacter");
|
|
char indexed[32] = "tex";
|
|
GetIndexingName(indexed, 12);
|
|
GetOldIndexingName(indexed, 7);
|
|
check(!strcmp(indexed, "tex127"), "GetIndexingName/GetOldIndexingName");
|
|
std::string local;
|
|
check(GetLocalFileName("D:\\ymir\\", "d:/ymir/a.dds", &local) && local == "a.dds", "GetLocalFileName");
|
|
int argc = 0;
|
|
PCHAR* argv = CommandLineToArgv(const_cast<PCHAR>("x \"y z\" w"), &argc);
|
|
check(argc == 3 && !strcmp(argv[1], "y z"), "CommandLineToArgv");
|
|
free(argv);
|
|
// FileLoader (40250 EterBase/FileLoader.cpp): Bind keeps a CP949 lead byte and its trail byte together,
|
|
// on hosts where char is unsigned too, and takes any pair of '\r'/'\n' as one line break ("\n\n" too).
|
|
{
|
|
const char text[] = "a \"b c\"\td\r\n\xB0\xA1\tx\n\n\nlast";
|
|
CMemoryTextFileLoader loader;
|
|
loader.Bind(int(sizeof(text) - 1), text);
|
|
check(loader.GetLineCount() == 4 && loader.CheckLineIndex(3) && !loader.CheckLineIndex(4), "TextFileLoader lines");
|
|
CTokenVector tokens;
|
|
check(loader.SplitLine(0, &tokens) && tokens.size() == 3 && tokens[1] == "b c" && tokens[2] == "d", "SplitLine quoted");
|
|
check(loader.SplitLine2(0, &tokens) == 0 && tokens.size() == 3, "SplitLine2");
|
|
check(loader.SplitLineByTab(1, &tokens) && tokens.size() == 2 && tokens[0] == "\xB0\xA1" && tokens[1] == "x", "SplitLineByTab CP949");
|
|
check(!loader.SplitLine(2, &tokens) && !loader.SplitLineByTab(2, &tokens), "empty line");
|
|
check(loader.GetLineString(3) == "last", "last line without newline");
|
|
|
|
const unsigned char bytes[] = {1, 2, 3, 4, 5};
|
|
CMemoryFileLoader mem(5, bytes);
|
|
unsigned char got[4] = {};
|
|
check(mem.Read(3, got) && got[2] == 3 && mem.GetPosition() == 3 && !mem.Read(3, got) && mem.GetSize() == 5, "MemoryFileLoader");
|
|
|
|
char path[] = "/tmp/mt_fileloader_XXXXXX";
|
|
int fd = mkstemp(path);
|
|
FILE * fp = fdopen(fd, "wb");
|
|
fwrite(bytes, 1, 5, fp);
|
|
fclose(fp);
|
|
CDiskFileLoader disk;
|
|
check(disk.Open(path) && disk.GetSize() == 5 && disk.Read(4, got) && got[3] == 4, "DiskFileLoader");
|
|
disk.Close();
|
|
check(!disk.Open("/nonexistent/mt_fileloader"), "DiskFileLoader missing");
|
|
remove(path);
|
|
}
|
|
|
|
// CRC32: the IEEE check value, GetCaseCRC32 folds ASCII case (effect/sound/tree name keys), and the
|
|
// file variants (guild symbol CRC + size, GuildMarkDownloader) CRC the whole file.
|
|
{
|
|
check(GetCRC32("123456789", 9) == 0xCBF43926u, "GetCRC32 check value");
|
|
check(GetCaseCRC32("D:/Ymir Work/A.MSE", 18) == GetCaseCRC32("d:/ymir work/a.mse", 18), "GetCaseCRC32 case fold");
|
|
check(GetCaseCRC32("abc", 3) == GetCRC32("ABC", 3), "GetCaseCRC32 folds to upper case");
|
|
|
|
char path[] = "/tmp/mt_crc32_XXXXXX";
|
|
int fd = mkstemp(path);
|
|
FILE * fp = fdopen(fd, "wb");
|
|
fwrite("123456789", 1, 9, fp);
|
|
fclose(fp);
|
|
check(GetFileCRC32(path) == 0xCBF43926u, "GetFileCRC32");
|
|
check(GetFileSize(path) == 9, "GetFileSize");
|
|
fp = fopen(path, "rb");
|
|
fseek(fp, 4, SEEK_SET);
|
|
check(GetHFILECRC32(fp) == 0xCBF43926u && ftell(fp) == 4, "GetHFILECRC32 whole file, position kept");
|
|
fclose(fp);
|
|
check(GetFileCRC32("/nonexistent/mt_crc32") == 0 && GetFileSize("/nonexistent/mt_crc32") == 0, "CRC32 missing file");
|
|
remove(path);
|
|
}
|
|
|
|
// CFileBase (Win32 HANDLE -> FILE*): write mode opens without truncating and tracks the size, Seek clamps
|
|
// to the size, SeekCur takes the DWORD as a signed distance, Read succeeds on a short read.
|
|
// CMappedFile: the view at a non-aligned offset, Seek clamps, Read refuses past the end,
|
|
// AppendDataBlock re-links onto a copy.
|
|
{
|
|
char path[] = "/tmp/mt_filebase_XXXXXX";
|
|
close(mkstemp(path));
|
|
{
|
|
CFileBase f;
|
|
check(f.IsNull(), "CFileBase IsNull before Create");
|
|
check(f.Create(path, CFileBase::FILEMODE_WRITE) && !f.IsNull(), "CFileBase Create write");
|
|
check(strcmp(f.GetFileName(), path) == 0, "CFileBase GetFileName");
|
|
check(f.Write("abcdefgh", 8) && f.Size() == 8 && f.GetPosition() == 8, "CFileBase Write size/position");
|
|
}
|
|
{
|
|
CFileBase f;
|
|
check(f.Create(path, CFileBase::FILEMODE_WRITE) && f.Size() == 8, "CFileBase write mode keeps contents");
|
|
f.Seek(100);
|
|
check(f.GetPosition() == 8, "CFileBase Seek clamps to size");
|
|
f.Seek(2);
|
|
f.SeekCur(3);
|
|
check(f.GetPosition() == 5, "CFileBase SeekCur forward");
|
|
f.SeekCur(DWORD(-2));
|
|
check(f.GetPosition() == 3, "CFileBase SeekCur negative distance");
|
|
char buf[8] = {};
|
|
check(f.Read(buf, 2) && buf[0] == 'd' && buf[1] == 'e', "CFileBase Read");
|
|
check(f.Read(buf, 8), "CFileBase short Read succeeds");
|
|
}
|
|
{
|
|
CFileBase f;
|
|
check(!f.Create("/nonexistent/mt_filebase", CFileBase::FILEMODE_READ) && f.IsNull(), "CFileBase missing file");
|
|
}
|
|
{
|
|
CMappedFile mf;
|
|
const void * view = NULL;
|
|
check(mf.Create(path, &view, 3, 4) && memcmp(view, "defg", 4) == 0, "CMappedFile view at offset");
|
|
check(mf.Get() == view && mf.GetPosition() == 3 && mf.Size() == 4, "CMappedFile Get/GetPosition/Size");
|
|
char buf[8] = {};
|
|
check(mf.Read(buf, 3) && memcmp(buf, "def", 3) == 0 && mf.GetSeekPosition() == 3, "CMappedFile Read");
|
|
check(!mf.Read(buf, 2) && mf.GetSeekPosition() == 3, "CMappedFile Read past end");
|
|
check(*mf.GetCurrentSeekPoint() == 'g', "CMappedFile GetCurrentSeekPoint");
|
|
check(mf.Seek(1, CMappedFile::SEEK_TYPE_CURRENT) == 4 && mf.Seek(9, CMappedFile::SEEK_TYPE_CURRENT) == 4, "CMappedFile Seek current clamps");
|
|
check(mf.Seek(1, CMappedFile::SEEK_TYPE_END) == 3 && mf.Seek(1) == 1, "CMappedFile Seek end/begin");
|
|
BYTE * joined = mf.AppendDataBlock("XY", 2);
|
|
check(mf.Size() == 6 && memcmp(joined, "defgXY", 6) == 0, "CMappedFile AppendDataBlock");
|
|
check(!mf.Create(path, &view, 6, 4), "CMappedFile view past the end");
|
|
}
|
|
remove(path);
|
|
}
|
|
return failures ? 1 : 0;
|
|
}
|