crosslang/src/archive.cpp

291 lines
10 KiB
C++

#include "CrossLang.hpp"
#include <iostream>
namespace Tesses::CrossLang
{
void CrossArchiveCreate(Tesses::Framework::Filesystem::VFS* vfs,Tesses::Framework::Streams::Stream* strm,std::string name, TVMVersion version, std::string info)
{
static std::vector<uint8_t> error_message_byte_code = {
PUSHSTRING,
0,
0,
0,
2,
GETVARIABLE,
PUSHSTRING,
0,
0,
0,
3,
PUSHSTRING,
0,
0,
0,
4,
PUSHLONG,
0,
0,
0,
0,
0,
0,
0,
1,
CALLMETHOD,
RET
};
auto writeInt = [](Tesses::Framework::Streams::Stream* strm,uint32_t number)->void{
uint8_t buff[4];
BitConverter::FromUint32BE(buff[0],number);
strm->WriteBlock(buff,4);
};
auto writeStr = [&writeInt](Tesses::Framework::Streams::Stream* strm,std::string text)->void {
writeInt(strm,(uint32_t)text.size());
strm->WriteBlock((const uint8_t*)text.c_str(),text.size());
};
std::vector<std::string> strs;
strs.push_back(name);
strs.push_back(info);
strs.push_back("Console");
strs.push_back("WriteLine");
strs.push_back("You are trying to run a crvm archive, stop it you won't get anywhere!\nUse crossarchiveextract instead.");
std::vector<std::string> resources;
auto ensureResource = [&resources](std::string path)->uint32_t {
for(uint32_t i = 0; i < (uint32_t)resources.size(); i++)
{
if(resources[i] == path) return i;
}
uint32_t index = (uint32_t)resources.size();
resources.push_back(path);
return index;
};
auto ensureString = [&strs](std::string str)->uint32_t {
for(uint32_t i = 0; i < (uint32_t)strs.size(); i++)
{
if(strs[i] == str) return i;
}
uint32_t index = (uint32_t)strs.size();
strs.push_back(str);
return index;
};
Tesses::Framework::Streams::MemoryStream ms(true);
std::function<void(Tesses::Framework::Filesystem::VFSPath)> walkFS = [vfs,&ensureString,&ensureResource,&ms,&walkFS,&writeInt](Tesses::Framework::Filesystem::VFSPath path)->void {
if(vfs->DirectoryExists(path))
{
ms.WriteByte(1);
std::vector<std::string> paths;
for(auto item : vfs->EnumeratePaths(path))
{
if(!item.relative && item.path.size() == 1 && item.path[0] == "__resdir_tmp") continue;
if(vfs->DirectoryExists(item) || vfs->RegularFileExists(item))
paths.push_back(item.GetFileName());
}
writeInt(&ms,(uint32_t)paths.size());
for(auto item : paths)
{
writeInt(&ms,ensureString(item));
walkFS(path / item);
}
}
else if(vfs->RegularFileExists(path))
{
ms.WriteByte(0);
writeInt(&ms,ensureResource(path.ToString()));
}
};
walkFS(std::string("/"));
uint8_t main_header[18];
memcpy(main_header,"TCROSSVM",8);
TVMVersion rtVersion(TVM_MAJOR,TVM_MINOR,TVM_PATCH,TVM_BUILD,TVM_VERSIONSTAGE);
rtVersion.ToArray(main_header+8);
version.ToArray(main_header+13);
strm->WriteBlock(main_header,sizeof(main_header));
writeInt(strm,(uint32_t)(5+resources.size()));
strm->WriteBlock((const uint8_t*)"STRS",4);
uint32_t sz=4;
for(auto str : strs)
sz += (uint32_t)(4 + str.size());
writeInt(strm,sz);
writeInt(strm,(uint32_t)strs.size());
for(auto str : strs)
{
writeStr(strm,str);
}
strm->WriteBlock((const uint8_t*)"NAME",4);
writeInt(strm,4);
writeInt(strm,0);
strm->WriteBlock((const uint8_t*)"INFO",4);
writeInt(strm,4);
writeInt(strm,1);
strm->WriteBlock((const uint8_t*)"CHKS",4);
writeInt(strm,(uint32_t)(12+error_message_byte_code.size()));
writeInt(strm,1);
writeInt(strm,0);
writeInt(strm,(uint32_t)error_message_byte_code.size());
strm->WriteBlock(error_message_byte_code.data(),error_message_byte_code.size());
for(auto res : resources)
{
strm->WriteBlock((const uint8_t*)"RESO",4);
auto strm2 = vfs->OpenFile(res,"rb");
writeInt(strm,(uint32_t)strm2->GetLength());
strm2->CopyTo(strm);
delete strm2;
}
strm->WriteBlock((const uint8_t*)"ARCV",4);
writeInt(strm,(uint32_t)ms.GetLength());
ms.Seek(0,Tesses::Framework::Streams::SeekOrigin::Begin);
ms.CopyTo(strm);
}
std::pair<std::pair<std::string,TVMVersion>,std::string> CrossArchiveExtract(Tesses::Framework::Streams::Stream* strm,Tesses::Framework::Filesystem::VFS* vfs)
{
auto ensure = [strm](uint8_t* buffer,size_t count)->void{
size_t read = strm->ReadBlock(buffer, count);
if(read < count) throw VMException("End of file, could not read " + std::to_string((int64_t)count) + " byte(s)., offset=" + std::to_string(strm->GetLength()));
};
auto ensureInt = [&ensure]()->uint32_t {
uint8_t buffer[4];
ensure(buffer,4);
return BitConverter::ToUint32BE(buffer[0]);
};
auto ensureStr = [&ensure,&ensureInt]()-> std::string {
auto len = ensureInt();
if(len == 0) return {};
std::string str={};
str.resize((size_t)len);
ensure((uint8_t*)str.data(),str.size());
return str;
};
std::vector<std::string> strs;
auto getStr = [&ensure,&ensureInt,&strs]()-> std::string {
auto index = ensureInt();
if(index > strs.size()) throw VMException("String does not exist in TCrossVM file, expected string index: " + std::to_string(index) + ", total strings: " + std::to_string(strs.size()));
return strs[index];
};
uint8_t main_header[18];
ensure(main_header,sizeof(main_header));
if(strncmp((const char*)main_header,"TCROSSVM",8) != 0) throw VMException("Invalid TCrossVM image.");
TVMVersion version(main_header+8);
if(version.CompareToRuntime() == 1)
{
throw VMException("Runtime is too old.");
}
TVMVersion v2(main_header+13);
std::string name;
std::string info;
size_t _len = (size_t)ensureInt();
char table_name[4];
uint32_t resource_id = 0;
Tesses::Framework::Filesystem::VFSPath tmpDir("/__resdir_tmp");
vfs->CreateDirectory(tmpDir);
for(size_t i = 0;i < _len; i++)
{
ensure((uint8_t*)table_name,sizeof(table_name));
size_t tableLen = (size_t)ensureInt();
if(strncmp(table_name,"NAME",4) == 0)
{
name = getStr();
}
else if(strncmp(table_name,"INFO",4) == 0)
{
info = getStr();
}
else if(strncmp(table_name,"RESO",4) == 0) //resources (using embed)
{
auto path = tmpDir /std::to_string(resource_id);
auto strm2 = vfs->OpenFile(path,"wb");
size_t read = 0;
size_t offset = 0;
uint8_t buff[1024];
do {
read = std::min(std::min(tableLen-offset,tableLen), sizeof(buff));
read = strm->Read(buff,read);
if(read > 0)
strm2->WriteBlock(buff,read);
offset+=read;
} while(read > 0);
delete strm2;
resource_id++;
}
else if(strncmp(table_name,"STRS",4) == 0) //strings
{
size_t strsLen = (size_t)ensureInt();
for(size_t j = 0;j < strsLen;j++)
{
strs.push_back(ensureStr());
}
}
else if(strncmp(table_name,"ARCV",4) == 0)
{
size_t offset = 0;
std::function<void(Tesses::Framework::Filesystem::VFSPath)> walkEntry = [strm,vfs,&getStr,&ensureInt,&tmpDir,&tableLen,&offset,&walkEntry](Tesses::Framework::Filesystem::VFSPath path)->void {
if(offset + 1 > tableLen) return;
uint8_t type = strm->ReadByte();
offset++;
if(type == 1)
{
//ISDIR
vfs->CreateDirectory(path);
if(offset + 4 > tableLen) return;
uint32_t count = ensureInt();
offset +=4;
for(uint32_t i = 0; i < count; i++)
{
if(offset + 4 > tableLen) return;
std::string name = getStr();
offset +=4;
walkEntry(path / name);
}
}
else if(type == 0)
{
if(offset + 4 > tableLen) return;
uint32_t index = ensureInt();
auto fSrc = tmpDir /std::to_string(index);
vfs->MoveFile(fSrc, path);
}
};
walkEntry(std::string("/"));
}
else
{
if(strm->CanSeek())
{
strm->Seek((int64_t)tableLen,Tesses::Framework::Streams::SeekOrigin::Current);
}
else{
uint8_t* buffer=new uint8_t[tableLen];
ensure(buffer,tableLen);
delete buffer;
}
}
}
vfs->DeleteDirectoryRecurse(tmpDir);
return std::pair<std::pair<std::string,TVMVersion>,std::string>(std::pair<std::string,TVMVersion>(name,v2),info);
}
}