This commit is contained in:
2025-08-31 05:03:04 -05:00
parent 1ca18ad0e1
commit c75c79e3e0
6 changed files with 2375 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
#!/bin/bash
mkdir -p build
cd build
mkdir build-deb-amd64
cmake -S ../../../ -B build-deb-amd64 -DCMAKE_INSTALL_PREFIX=/usr -DCROSSLANG_FETCHCONTENT=OFF -DCROSSLANG_ENABLE_FFI=ON
cd build-deb-amd64
make -j`nproc`
make install DESTDIR=../crosslang_1.0.0_amd64
mkdir -p ../crosslang_1.0.0_amd64/DEBIAN
mkdir -p ../crosslang_1.0.0_amd64/usr/share/Tesses/CrossLang
wget -O ../crosslang_1.0.0_amd64/usr/share/Tesses/CrossLang/Tesses.CrossLang.ShellPackage-1.0.0.0-prod.crvm https://downloads.tesses.net/ShellPackage.crvm
cp ../../debian/control-amd64 ../crosslang_1.0.0_amd64/DEBIAN/control
cd ../
dpkg-deb --build crosslang_1.0.0_amd64

View File

@@ -0,0 +1,8 @@
Package: crosslang
Version: 1.0.0
Architecture: amd64
Essential: no
Priority: optional
Depends: tessesframework, libffi-dev
Maintainer: Mike Nolan
Description: A programming language

View File

@@ -0,0 +1,6 @@
#!/bin/bash
curl --user tesses50:$GITEA_AUTH -X DELETE \
https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/crosslang/1.0.0/amd64
curl --user tesses50:$GITEA_AUTH \
--upload-file build/crosslang_1.0.0_amd64.deb \
https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/upload

1282
src/assembler/asm.cpp Normal file

File diff suppressed because it is too large Load Diff

894
src/assembler/disasm.cpp Normal file
View File

@@ -0,0 +1,894 @@
#include "CrossLang.hpp"
namespace Tesses::CrossLang {
class CrossLangFileReader {
Tesses::Framework::Streams::Stream* strm;
void Ensure(uint8_t* buffer, size_t len)
{
auto read = this->strm->ReadBlock(buffer, len);
if(read < len) throw VMException("End of file, could not read " + std::to_string((int64_t)len) + " byte(s)., offset=" + std::to_string(strm->GetLength()));
}
uint32_t ReadInt()
{
uint8_t buffer[4];
Ensure(buffer,4);
return BitConverter::ToUint32BE(buffer[0]);
}
std::string ReadString()
{
auto len = ReadInt();
if(len == 0) return {};
std::string str={};
str.resize((size_t)len);
Ensure((uint8_t*)str.data(),str.size());
return str;
}
std::string GetString()
{
uint32_t index=ReadInt();
if(index >= this->strings.size()) throw VMException("String does not exist in TCrossVM file, expected string index: " + std::to_string(index) + ", total strings: " + std::to_string(this->strings.size()));
return this->strings[index];
}
public:
CrossLangFileReader(Tesses::Framework::Streams::Stream* strm)
{
this->strm = strm;
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);
this->version = v2;
size_t _len = (size_t)ReadInt();
char table_name[4];
for(size_t i = 0;i < _len; i++)
{
Ensure((uint8_t*)table_name,sizeof(table_name));
size_t tableLen = (size_t)ReadInt();
if(strncmp(table_name,"NAME",4) == 0)
{
this->name = GetString();
}
else if(strncmp(table_name,"INFO",4) == 0)
{
this->info = GetString();
}
else if(strncmp(table_name,"DEPS",4) == 0) //dependencies
{
std::string name = GetString();
uint8_t version_bytes[5];
Ensure(version_bytes,sizeof(version_bytes));
TVMVersion depVersion(version_bytes);
this->dependencies.push_back(std::pair<std::string,TVMVersion>(name, depVersion));
}
else if(strncmp(table_name,"TOOL",4) == 0) //compile tools (for package manager)
{
std::string name = GetString();
uint8_t version_bytes[5];
Ensure(version_bytes,sizeof(version_bytes));
TVMVersion depVersion(version_bytes);
this->tools.push_back(std::pair<std::string,TVMVersion>(name, depVersion));
}
else if(strncmp(table_name,"RESO",4) == 0) //resources (using embed)
{
std::vector<uint8_t> data;
data.resize(tableLen);
Ensure(data.data(), tableLen);
this->resources.push_back(data);
}
else if(strncmp(table_name,"CHKS",4) == 0) //chunks
{
size_t chunkCount = (size_t)ReadInt();
for(size_t j = 0; j < chunkCount; j++)
{
std::vector<std::string> args;
size_t argCount = (size_t)ReadInt();
for(size_t k = 0; k < argCount; k++)
{
args.push_back(GetString());
}
std::vector<uint8_t> code;
size_t len = (size_t)ReadInt();
code.resize(len);
Ensure(code.data(),len);
//reader.ReadIntoBuffer(chunk->code);
this->chunks.emplace(this->chunks.end(),args,code);
}
}
else if(strncmp(table_name,"FUNS",4) == 0) //functions
{
size_t funLength = (size_t)ReadInt();
for(size_t j = 0; j < funLength;j++)
{
std::vector<std::string> fnParts;
uint32_t fnPartsC = ReadInt();
for(uint32_t k = 0; k < fnPartsC; k++)
{
fnParts.push_back(GetString());
}
uint32_t fnNumber = ReadInt();
this->functions.push_back(std::pair<std::vector<std::string>,uint32_t>(fnParts,fnNumber));
}
}
else if(strncmp(table_name,"STRS",4) == 0) //strings
{
size_t strsLen = (size_t)ReadInt();
for(size_t j = 0;j < strsLen;j++)
{
this->strings.push_back(ReadString());
}
}
else if(strncmp(table_name,"ICON",4) == 0) //icon
{
this->icon = (int32_t)ReadInt();
}
else if(strncmp(table_name,"MACH",4) == 0) //machine
{
std::string name = GetString();
std::string howToGet = GetString();
this->vms.push_back(std::pair<std::string,std::string>(name,howToGet));
}
else if(strncmp(table_name,"CLSS",4) == 0) //classes
{
uint32_t clsCnt = ReadInt();
for(uint32_t j = 0; j < clsCnt; j++)
{
TClass cls;
cls.documentation= GetString();
uint32_t name_cnt = ReadInt();
for(uint32_t k = 0; k < name_cnt; k++)
{
cls.name.push_back(GetString());
}
name_cnt = ReadInt();
for(uint32_t k = 0; k < name_cnt; k++)
{
cls.inherits.push_back(GetString());
}
name_cnt = ReadInt();
for(uint32_t k = 0; k < name_cnt; k++)
{
TClassEntry ent;
Ensure(main_header,1);
uint8_t sig = main_header[0];
ent.isAbstract = (sig & 0b00001000) != 0;
ent.isFunction = (sig & 0b00000100) == 0;
ent.modifier = (TClassModifier)(sig & 3);
ent.documentation = GetString();
ent.name = GetString();
uint32_t arglen = ReadInt();
for(uint32_t l = 0; l < arglen; l++)
ent.args.push_back(GetString());
ent.chunkId = ReadInt();
cls.entry.push_back(ent);
}
this->classes.push_back(cls);
}
}
else
{
std::vector<uint8_t> data;
data.resize(tableLen);
Ensure(data.data(), tableLen);
std::string key(std::string(table_name), 4);
this->sections.push_back(std::pair<std::string,std::vector<uint8_t>>(key,data));
}
}
}
std::vector<std::string> strings;
std::vector<std::pair<std::vector<std::string>,std::vector<uint8_t>>> chunks;
std::vector<std::pair<std::string,std::string>> vms;
std::vector<std::pair<std::vector<std::string>, uint32_t>> functions;
std::vector<std::pair<std::string,TVMVersion>> dependencies;
std::vector<std::pair<std::string,TVMVersion>> tools;
std::vector<std::pair<std::string,std::vector<uint8_t>>> sections;
std::vector<std::vector<uint8_t>> resources;
std::vector<TClass> classes;
std::string name;
TVMVersion version;
std::string info;
int32_t icon;
std::string Chunk2String(size_t chunkId,size_t tab)
{
if(chunkId >= this->chunks.size()) return {};
auto& code = this->chunks[chunkId].second;
std::string buffer = {};
std::vector<uint32_t> labels;
size_t i = 0;
while(i < code.size())
{
switch(code[i++])
{
case PUSHRESOURCE:
case PUSHSTRING:
case SCOPEENDTIMES:
case PUSHCLOSURE:
case PUSHSCOPELESSCLOSURE:
i+=4;
break;
case PUSHLONG:
case PUSHDOUBLE:
i+=8;
break;
case PUSHCHAR:
i+=1;
break;
case JMP:
case JMPC:
case JMPIFBREAK:
case JMPIFCONTINUE:
case JMPIFDEFINED:
case JMPUNDEFINED:
{
bool already = false;
for(auto item : labels)
{
if(item == i) {
already=true;
break;
}
}
if(!already)
labels.push_back(BitConverter::ToUint32BE(code[i]));
i+=4;
}
break;
default:
break;
}
}
i=0;
while(i < code.size())
{
for(size_t j = 0; j < labels.size(); j++)
{
if(labels[j] == i)
{
buffer.append(tab,'\t');
buffer.append("lbl l" + std::to_string((uint32_t)j) + "\n");
break;
}
}
buffer.append(tab,'\t');
switch(code[i++])
{
case ADD:
buffer.append("add");
break;
case SUB:
buffer.append("sub");
break;
case TIMES:
buffer.append("mul");
break;
case DIVIDE:
buffer.append("div");
break;
case MODULO:
buffer.append("mod");
break;
case LEFTSHIFT:
buffer.append("lsh");
break;
case RIGHTSHIFT:
buffer.append("rsh");
break;
case BITWISEOR:
buffer.append("bor");
break;
case BITWISEAND:
buffer.append("band");
break;
case BITWISENOT:
buffer.append("bnot");
break;
case LESSTHAN:
buffer.append("lt");
break;
case GREATERTHAN:
buffer.append("gt");
break;
case LESSTHANEQ:
buffer.append("lte");
break;
case GREATERTHANEQ:
buffer.append("gte");
break;
case EQ:
buffer.append("eq");
break;
case NEQ:
buffer.append("neq");
break;
case NOT:
buffer.append("not");
break;
case NEGATIVE:
buffer.append("neg");
break;
case XOR:
buffer.append("xor");
break;
case POP:
buffer.append("pop");
break;
case DUP:
buffer.append("dup");
break;
case PUSHCLOSURE:
{
uint32_t clId = (uint32_t)code[i++] << 24;
clId |= (uint32_t)code[i++] << 16;
clId |= (uint32_t)code[i++] << 8;
clId |= (uint32_t)code[i++];
buffer.append("pushclosure (");
bool first=true;
for(auto item : this->chunks.at(clId).first)
{
if(!first)
buffer.append(", ");
buffer.append(item);
first=false;
}
buffer.append(") {\n");
buffer.append(Chunk2String((size_t)clId,tab+1));
buffer.append(tab,'\t');
buffer.append("}");
}
break;
case CREATEDICTIONARY:
buffer.append("createdict");
break;
case CREATEARRAY:
buffer.append("createarray");
break;
case APPENDLIST:
buffer.append("appendlist");
break;
case APPENDDICT:
buffer.append("appenddict");
break;
case PUSHRESOURCE:
{
uint32_t clId = (uint32_t)code[i++] << 24;
clId |= (uint32_t)code[i++] << 16;
clId |= (uint32_t)code[i++] << 8;
clId |= (uint32_t)code[i++];
buffer.append("embed ");
buffer.append(EscapeString(name + "-" + version.ToString()+"_"+ std::to_string(clId) + ".bin",true));
}
break;
case PUSHLONG:
{
uint64_t number = (uint64_t)code[i++] << 56;
number |= (uint64_t)code[i++] << 48;
number |= (uint64_t)code[i++] << 40;
number |= (uint64_t)code[i++] << 32;
number |= (uint64_t)code[i++] << 24;
number |= (uint64_t)code[i++] << 16;
number |= (uint64_t)code[i++] << 8;
number |= (uint64_t)code[i++];
buffer.append("push " + std::to_string(number));
}
break;
case PUSHCHAR:
{
buffer.append("push '" + EscapeString(std::string({(char)code[i++]}),false) + "'");
}
break;
case PUSHDOUBLE:
{
auto res = BitConverter::ToDoubleBE(code[i]);
i+=8;
buffer.append("push " + std::to_string(res));
}
break;
case PUSHSTRING:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("push " + EscapeString(strings[res],true));
}
break;
case PUSHNULL:
buffer.append("push null");
break;
case PUSHUNDEFINED:
buffer.append("push undefined");
break;
case SCOPEBEGIN:
buffer.append("scopebegin");
break;
case SCOPEEND:
buffer.append("scopeend");
break;
case SCOPEENDTIMES:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("scopeendtimes " + std::to_string(res));
}
break;
case PUSHFALSE:
buffer.append("push false");
break;
case PUSHTRUE:
buffer.append("push true");
break;
case SETVARIABLE:
buffer.append("setvariable");
break;
case GETVARIABLE:
buffer.append("getvariable");
break;
case DECLAREVARIABLE:
buffer.append("declarevariable");
break;
case SETFIELD:
buffer.append("setfield");
break;
case GETFIELD:
buffer.append("getfield");
break;
case CALLFUNCTION:
buffer.append("callfunction");
break;
case CALLMETHOD:
buffer.append("callmethod");
break;
case RET:
buffer.append("ret");
break;
case JMPC:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("jmpc l");
for(size_t j = 0; j < labels.size(); j++)
{
if(labels[j] == res)
{
buffer.append(std::to_string((uint32_t)j));
break;
}
}
}
break;
case JMP:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("jmp l");
for(size_t j = 0; j < labels.size(); j++)
{
if(labels[j] == res)
{
buffer.append(std::to_string((uint32_t)j));
break;
}
}
}
break;
case JMPUNDEFINED:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("jmpundefined l");
for(size_t j = 0; j < labels.size(); j++)
{
if(labels[j] == res)
{
buffer.append(std::to_string((uint32_t)j));
break;
}
}
}
break;
case DEFER:
buffer.append("defer");
break;
case TRYCATCH:
buffer.append("trycatch");
break;
case THROW:
buffer.append("throw");
break;
case PUSHSCOPELESSCLOSURE:
{
uint32_t clId = (uint32_t)code[i++] << 24;
clId |= (uint32_t)code[i++] << 16;
clId |= (uint32_t)code[i++] << 8;
clId |= (uint32_t)code[i++];
buffer.append("pushscopelessclosure (");
bool first=true;
for(auto item : this->chunks.at(clId).first)
{
if(!first)
buffer.append(", ");
buffer.append(item);
first=false;
}
buffer.append(") {\n");
buffer.append(Chunk2String((size_t)clId,tab+1));
buffer.append(tab,'\t');
buffer.append("}");
}
break;
case YIELD:
buffer.append("yield");
break;
case PUSHROOTPATH:
buffer.append("push /");
break;
case PUSHRELATIVEPATH:
buffer.append("push .");
break;
case BREAKPOINT:
buffer.append("breakpoint");
break;
case PUSHBREAK:
buffer.append("push break");
break;
case PUSHCONTINUE:
buffer.append("push continue");
break;
case JMPIFBREAK:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("jmpifbreak l");
for(size_t j = 0; j < labels.size(); j++)
{
if(labels[j] == res)
{
buffer.append(std::to_string((uint32_t)j));
break;
}
}
}
break;
case JMPIFCONTINUE:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("jmpifcontinue l");
for(size_t j = 0; j < labels.size(); j++)
{
if(labels[j] == res)
{
buffer.append(std::to_string((uint32_t)j));
break;
}
}
}
break;
case JMPIFDEFINED:
{
auto res = BitConverter::ToUint32BE(code[i]);
i+=4;
buffer.append("jmpifdefined l");
for(size_t j = 0; j < labels.size(); j++)
{
if(labels[j] == res)
{
buffer.append(std::to_string((uint32_t)j));
break;
}
}
}
break;
}
buffer.push_back('\n');
}
if(code.empty()) buffer.push_back('\n');
return buffer;
}
};
void Disassemble(Tesses::Framework::Streams::Stream* src,Tesses::Framework::Filesystem::VFS* vfs, bool generateJSON,bool extractResources)
{
using namespace Tesses::Framework::Filesystem;
CrossLangFileReader file(src);
if(extractResources)
{
std::string resdir = "res";
VFSPath path=resdir;
vfs->CreateDirectory(path);
for(size_t i = 0; i < file.resources.size(); i++)
{
auto path2 = path / file.name + "-" + file.version.ToString()+"_"+ std::to_string((uint32_t)i) + ".bin";
auto strm = vfs->OpenFile(path2,"wb");
strm->WriteBlock(file.resources[i].data(),file.resources[i].size());
delete strm;
}
std::string secdir = "sections";
VFSPath secpath=secdir;
vfs->CreateDirectory(secpath);
for(size_t i = 0; i < file.sections.size(); i++)
{
auto path2 = secpath / file.name + "-" + file.version.ToString()+"_"+ std::to_string((uint32_t)i) + ".tsec";
if(file.sections[i].first.size() != 4) {
throw std::runtime_error("Chunk name is not 4 bytes");
}
auto strm = vfs->OpenFile(path2,"wb");
strm->WriteBlock((const uint8_t*)file.sections[i].first.data(), 4);
strm->WriteBlock(file.sections[i].second.data(),file.sections[i].second.size());
delete strm;
}
}
std::string srcFile = {};
srcFile.append("root {\n");
srcFile.append(file.Chunk2String(0,1));
srcFile.append("}\n");
for(auto& item : file.functions)
{
if(!item.first[0].empty())
{
srcFile.append("/^" + Tesses::Framework::Http::HttpUtils::Replace(item.first[0],"^","^^") +"^/\n");
}
srcFile.append("func ");
for(size_t i = 1; i < item.first.size(); i++)
{
if(i > 1)
{
srcFile.push_back('.');
}
srcFile.append(item.first[i]);
}
srcFile.append("(");
auto& chunk = file.chunks.at(item.second);
for(size_t i = 0; i < chunk.first.size(); i++)
{
if(i > 0)
{
srcFile.append(", ");
}
srcFile.append(chunk.first[i]);
}
srcFile.append(") {\n");
srcFile.append(file.Chunk2String((size_t)item.second,1));
srcFile.append("}\n");
}
for(auto& cls : file.classes)
{
if(!cls.documentation.empty())
{
srcFile.append("/^" + Tesses::Framework::Http::HttpUtils::Replace(cls.documentation,"^","^^") +"^/\n");
}
srcFile.append("class ");
for(size_t i = 0; i < cls.name.size(); i++)
{
if(i > 0)
{
srcFile.push_back('.');
}
srcFile.append(cls.name[i]);
}
if(!(cls.inherits.size() == 1 && cls.inherits[0] == "ClassObject"))
{
srcFile.append(" : ");
for(size_t i = 0; i < cls.inherits.size(); i++)
{
if(i > 0)
{
srcFile.push_back('.');
}
srcFile.append(cls.inherits[i]);
}
}
srcFile.append(" {\n");
for(auto& item : cls.entry)
{
if(!item.documentation.empty())
srcFile.append("\t/^"+Tesses::Framework::Http::HttpUtils::Replace(item.documentation,"^","^^")+"^/\n");
switch(item.modifier)
{
case TClassModifier::Private:
srcFile.append("\tprivate ");
break;
case TClassModifier::Protected:
srcFile.append("\tprotected ");
break;
case TClassModifier::Public:
srcFile.append("\tpublic ");
break;
case TClassModifier::Static:
srcFile.append("\tstatic ");
default:
break;
}
if(item.isFunction)
{
if(item.isAbstract)
{
srcFile.append("abstract " + item.name);
srcFile.append("(");
for(size_t i = 0; i < item.args.size(); i++)
{
if(i > 0)
{
srcFile.append(", ");
}
srcFile.append(item.args[i]);
}
srcFile.append(");\n");
}
else {
srcFile.append(item.name);
srcFile.append("(");
for(size_t i = 0; i < item.args.size(); i++)
{
if(i > 0)
{
srcFile.append(", ");
}
srcFile.append(item.args[i]);
}
srcFile.append(") {\n");
srcFile.append(file.Chunk2String(item.chunkId,2));
srcFile.append("\t}\n");
}
}
else {
srcFile.append(item.name);
if(item.isAbstract)
{
srcFile.append(";\n");
}
else {
srcFile.append(" {\n");
srcFile.append(file.Chunk2String(item.chunkId,2));
srcFile.append("\t}\n");
}
}
}
srcFile.append("}\n");
}
std::string srcdirs = "src";
VFSPath srcdir=srcdirs;
vfs->CreateDirectory(srcdir);
Tesses::Framework::TextStreams::StreamWriter writer(vfs->OpenFile(srcdir / file.name + "-" + file.version.ToString() + ".tcasm","wb"),true);
writer.Write(srcFile);
if(generateJSON)
{
using namespace Tesses::Framework::Serialization::Json;
JObject json_data {
JOItem{"name", file.name},
JOItem{"version",file.version.ToString()}
};
if(!file.info.empty())
{
json_data.SetValue("info",Json::Decode(file.info));
}
if(file.icon > -1 && file.icon < file.resources.size())
{
json_data.SetValue("icon",file.name + "-" + file.version.ToString()+"_"+ std::to_string(file.icon) + ".bin");
}
if(!file.dependencies.empty())
{
JArray array;
for(auto& item : file.dependencies)
{
array.Add(JObject {
JOItem {
"name",
item.first
},
JOItem {
"version",
item.second.ToString()
}
});
}
json_data.SetValue("dependencies", array);
}
if(!file.tools.empty())
{
JArray array;
for(auto& item : file.tools)
{
array.Add(JObject {
JOItem {
"name",
item.first
},
JOItem {
"version",
item.second.ToString()
}
});
}
json_data.SetValue("tools", array);
}
if(!file.vms.empty())
{
JArray array;
for(auto& item : file.vms)
{
array.Add(JObject {
JOItem {
"name",
item.first
},
JOItem {
"how_to_get",
item.second
}
});
}
json_data.SetValue("vms", array);
}
Tesses::Framework::TextStreams::StreamWriter json_writer(vfs->OpenFile(VFSPath() / "crossapp.json","wb" ),true);
json_writer.WriteLine(Json::Encode(json_data,true));
}
}
}

171
src/assembler/merge.cpp Normal file
View File

@@ -0,0 +1,171 @@
#include "CrossLang.hpp"
using namespace Tesses::Framework::Serialization::Json;
namespace Tesses::CrossLang {
static void LoadDependency(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir, std::pair<std::string,TVMVersion> dep, std::vector<std::pair<std::string, TVMVersion>>& files, std::vector<std::pair<std::string, TVMVersion>>& tools);
static void LoadDependencies(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir,TFile* file, std::vector<std::pair<std::string, TVMVersion>>& files, std::vector<std::pair<std::string, TVMVersion>>& tools);
static void LoadDependency(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir, std::pair<std::string,TVMVersion> dep, std::vector<std::pair<std::string, TVMVersion>>& files, std::vector<std::pair<std::string, TVMVersion>>& tools)
{
for(auto index = files.begin(); index != files.end(); index++)
{
if(index->first == dep.first)
{
if(index->second.CompareTo(dep.second) >= 0) return;
files.erase(index);
break;
}
}
std::string name = {};
name.append(dep.first);
name.push_back('-');
name.append(dep.second.ToString());
name.append(".crvm");
auto filename= sourceDir / name;
if(srcFS->RegularFileExists(filename))
{
Tesses::Framework::Streams::Stream* file = srcFS->OpenFile(filename,"rb");
TFile f;
f.Load(nullptr, file);
delete file;
LoadDependencies(srcFS,sourceDir,&f,files,tools);
}
else throw VMException("Could not open file: \"" + name + "\".");
}
static void LoadDependencies(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir,TFile* file, std::vector<std::pair<std::string, TVMVersion>>& files, std::vector<std::pair<std::string, TVMVersion>>& tools)
{
files.push_back(std::pair<std::string,TVMVersion>(file->name,file->version));
for(auto item : file->tools)
{
bool exists = false;
for(auto& itm2 : tools)
{
if(itm2.first == item.first)
{
exists=true;
if(itm2.second.CompareTo(item.second) < 0) {
itm2.second = item.second;
}
break;
}
}
if(!exists)
tools.push_back(item);
}
for(auto item : file->dependencies)
{
LoadDependency(srcFS,sourceDir,item,files,tools);
}
}
static void EnumerateCRVM(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir,std::string filename, std::vector<std::pair<std::string, TVMVersion>>& files, Tesses::Framework::Filesystem::VFS* destFS)
{
TFile file;
auto strm = srcFS->OpenFile(sourceDir / filename,"rb");
if(strm->EndOfStream()) {
delete strm;
throw std::runtime_error("File does not exist: " + (sourceDir / filename).ToString() );
}
file.Load(nullptr,strm);
delete strm;
std::vector<std::pair<std::string, TVMVersion>> tools;
LoadDependencies(srcFS,sourceDir,&file,files,tools);
JObject json_data {
JOItem{"name", file.name},
JOItem{"version",file.version.ToString()}
};
if(!file.info.empty())
{
json_data.SetValue("info",Json::Decode(file.info));
}
if(file.icon > -1 && file.icon < file.resources.size())
{
json_data.SetValue("icon",file.name + "-" + file.version.ToString()+"_"+ std::to_string(file.icon) + ".bin");
}
if(!file.tools.empty())
{
JArray array;
for(auto& item : file.tools)
{
array.Add(JObject {
JOItem {
"name",
item.first
},
JOItem {
"version",
item.second.ToString()
}
});
}
json_data.SetValue("tools", array);
}
if(!file.vms.empty())
{
JArray array;
for(auto& item : file.vms)
{
array.Add(JObject {
JOItem {
"name",
item.first
},
JOItem {
"how_to_get",
item.second
}
});
}
json_data.SetValue("vms", array);
}
Tesses::Framework::TextStreams::StreamWriter json_writer(destFS->OpenFile(Tesses::Framework::Filesystem::VFSPath() / "crossapp.json","wb" ),true);
json_writer.WriteLine(Json::Encode(json_data,true));
}
Tesses::Framework::Filesystem::VFSPath Merge(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourcePath, Tesses::Framework::Filesystem::VFS* destFS)
{
std::vector<std::pair<std::string, TVMVersion>> files;
for(auto ent : destFS->EnumeratePaths(Tesses::Framework::Filesystem::VFSPath()))
{
if(destFS->DirectoryExists(ent))
{
destFS->DeleteDirectoryRecurse(ent);
}
else {
destFS->DeleteFile(ent);
}
}
EnumerateCRVM(srcFS,sourcePath.GetParent(), sourcePath.GetFileName(),files,destFS);
for(auto item : files)
{
auto filePath = sourcePath.GetParent() / item.first + "-" + item.second.ToString() + ".crvm";
if(srcFS->RegularFileExists(filePath))
{
auto strm = srcFS->OpenFile(filePath,"rb");
Disassemble(strm,destFS,false);
}
}
return Assemble(destFS);
}
}