У TexPipe убрано ключевое слово tex
This commit is contained in:
@@ -1246,7 +1246,7 @@ public:
|
|||||||
assert(vkInst);
|
assert(vkInst);
|
||||||
assert(serverSession);
|
assert(serverSession);
|
||||||
|
|
||||||
CMG.changeThreadsCount(1);
|
CMG.changeThreadsCount(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ChunkPreparator() {
|
~ChunkPreparator() {
|
||||||
|
|||||||
@@ -162,92 +162,6 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto resolveModelInfo = [&](std::string_view domain, std::string_view key) -> const ResourceFindInfo* {
|
|
||||||
auto& table = resourcesFirstStage[static_cast<size_t>(AssetType::Model)];
|
|
||||||
auto iterDomain = table.find(std::string(domain));
|
|
||||||
if(iterDomain == table.end())
|
|
||||||
return nullptr;
|
|
||||||
auto iterKey = iterDomain->second.find(std::string(key));
|
|
||||||
if(iterKey == iterDomain->second.end())
|
|
||||||
return nullptr;
|
|
||||||
return &iterKey->second;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::function<std::optional<js::object>(std::string_view, std::string_view, std::unordered_set<std::string>&)> loadModelProfile;
|
|
||||||
loadModelProfile = [&](std::string_view domain, std::string_view key, std::unordered_set<std::string>& visiting)
|
|
||||||
-> std::optional<js::object>
|
|
||||||
{
|
|
||||||
std::string fullKey = std::string(domain) + ':' + std::string(key);
|
|
||||||
if(!visiting.insert(fullKey).second) {
|
|
||||||
LOG.warn() << "Model parent cycle: " << fullKey;
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ResourceFindInfo* info = resolveModelInfo(domain, key);
|
|
||||||
if(!info) {
|
|
||||||
LOG.warn() << "Model file not found for parent: " << fullKey;
|
|
||||||
visiting.erase(fullKey);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceFile file = readFileBytes(info->Path);
|
|
||||||
std::string_view view(reinterpret_cast<const char*>(file.Data.data()), file.Data.size());
|
|
||||||
js::object obj = js::parse(view).as_object();
|
|
||||||
|
|
||||||
if(auto parentVal = obj.if_contains("parent")) {
|
|
||||||
if(parentVal->is_string()) {
|
|
||||||
std::string parentStr = std::string(parentVal->as_string());
|
|
||||||
auto [pDomain, pKeyRaw] = parseDomainKey(parentStr, domain);
|
|
||||||
fs::path pKeyPath = fs::path(pKeyRaw);
|
|
||||||
if(pKeyPath.extension().empty())
|
|
||||||
pKeyPath += ".json";
|
|
||||||
std::string pKey = pKeyPath.generic_string();
|
|
||||||
|
|
||||||
std::optional<js::object> parent = loadModelProfile(pDomain, pKey, visiting);
|
|
||||||
if(parent) {
|
|
||||||
auto mergeFieldIfMissing = [&](const char* field) {
|
|
||||||
if(!obj.contains(field) && parent->contains(field))
|
|
||||||
obj[field] = parent->at(field);
|
|
||||||
};
|
|
||||||
|
|
||||||
mergeFieldIfMissing("cuboids");
|
|
||||||
mergeFieldIfMissing("sub_models");
|
|
||||||
mergeFieldIfMissing("gui_light");
|
|
||||||
mergeFieldIfMissing("ambient_occlusion");
|
|
||||||
|
|
||||||
if(auto parentTextures = parent->if_contains("textures"); parentTextures && parentTextures->is_object()) {
|
|
||||||
if(auto childTextures = obj.if_contains("textures"); childTextures && childTextures->is_object()) {
|
|
||||||
auto& childObj = childTextures->as_object();
|
|
||||||
const auto& parentObj = parentTextures->as_object();
|
|
||||||
for(const auto& [tkey, tval] : parentObj) {
|
|
||||||
if(!childObj.contains(tkey))
|
|
||||||
childObj.emplace(tkey, tval);
|
|
||||||
}
|
|
||||||
} else if(!obj.contains("textures")) {
|
|
||||||
obj["textures"] = parentTextures->as_object();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(auto parentDisplay = parent->if_contains("display"); parentDisplay && parentDisplay->is_object()) {
|
|
||||||
if(auto childDisplay = obj.if_contains("display"); childDisplay && childDisplay->is_object()) {
|
|
||||||
auto& childObj = childDisplay->as_object();
|
|
||||||
const auto& parentObj = parentDisplay->as_object();
|
|
||||||
for(const auto& [dkey, dval] : parentObj) {
|
|
||||||
if(!childObj.contains(dkey))
|
|
||||||
childObj.emplace(dkey, dval);
|
|
||||||
}
|
|
||||||
} else if(!obj.contains("display")) {
|
|
||||||
obj["display"] = parentDisplay->as_object();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visiting.erase(fullKey);
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Функция парсинга ресурсов
|
// Функция парсинга ресурсов
|
||||||
auto buildResource = [&](AssetType type, std::string_view domain, std::string_view key, const ResourceFindInfo& info) -> PendingResource {
|
auto buildResource = [&](AssetType type, std::string_view domain, std::string_view key, const ResourceFindInfo& info) -> PendingResource {
|
||||||
PendingResource out;
|
PendingResource out;
|
||||||
@@ -268,25 +182,11 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass
|
|||||||
return getId(AssetType::Texture, mDomain, mKey);
|
return getId(AssetType::Texture, mDomain, mKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto normalizeTexturePipelineSrc = [](std::string_view src) -> std::string {
|
|
||||||
std::string out(src);
|
|
||||||
auto isSpace = [](unsigned char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; };
|
|
||||||
size_t start = 0;
|
|
||||||
while(start < out.size() && isSpace(static_cast<unsigned char>(out[start])))
|
|
||||||
++start;
|
|
||||||
if(out.compare(start, 3, "tex") != 0) {
|
|
||||||
std::string pref = "tex ";
|
|
||||||
pref += out.substr(start);
|
|
||||||
return pref;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::function<std::vector<uint8_t>(const std::string_view)> textureResolver
|
std::function<std::vector<uint8_t>(const std::string_view)> textureResolver
|
||||||
= [&](const std::string_view texturePipelineSrc) -> std::vector<uint8_t>
|
= [&](const std::string_view texturePipelineSrc) -> std::vector<uint8_t>
|
||||||
{
|
{
|
||||||
TexturePipelineProgram tpp;
|
TexturePipelineProgram tpp;
|
||||||
bool flag = tpp.compile(normalizeTexturePipelineSrc(texturePipelineSrc));
|
bool flag = tpp.compile(texturePipelineSrc);
|
||||||
if(!flag)
|
if(!flag)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@@ -307,13 +207,9 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass
|
|||||||
} else if (type == AssetType::Model) {
|
} else if (type == AssetType::Model) {
|
||||||
const std::string ext = info.Path.extension().string();
|
const std::string ext = info.Path.extension().string();
|
||||||
if (ext == ".json") {
|
if (ext == ".json") {
|
||||||
std::unordered_set<std::string> visiting;
|
ResourceFile file = readFileBytes(info.Path);
|
||||||
std::optional<js::object> objOpt = loadModelProfile(domain, key, visiting);
|
std::string_view view(reinterpret_cast<const char*>(file.Data.data()), file.Data.size());
|
||||||
if(!objOpt) {
|
js::object obj = js::parse(view).as_object();
|
||||||
LOG.warn() << "Не удалось загрузить модель: " << info.Path.string();
|
|
||||||
throw std::runtime_error("Model profile load failed");
|
|
||||||
}
|
|
||||||
js::object obj = std::move(*objOpt);
|
|
||||||
|
|
||||||
HeadlessModel hm;
|
HeadlessModel hm;
|
||||||
out.Header = hm.parse(obj, modelResolver, textureResolver);
|
out.Header = hm.parse(obj, modelResolver, textureResolver);
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ public:
|
|||||||
std::string Name;
|
std::string Name;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool compile(std::string src, std::string* err = nullptr) {
|
bool compile(std::string_view src, std::string* err = nullptr) {
|
||||||
Source_ = std::move(src);
|
Source_ = src;
|
||||||
Code_.clear();
|
Code_.clear();
|
||||||
Patches_.clear();
|
Patches_.clear();
|
||||||
PendingSub_.clear();
|
PendingSub_.clear();
|
||||||
@@ -423,7 +423,7 @@ public:
|
|||||||
if(src.Kind != SrcKind::TexId)
|
if(src.Kind != SrcKind::TexId)
|
||||||
return true;
|
return true;
|
||||||
if(src.TexId >= remap.size()) {
|
if(src.TexId >= remap.size()) {
|
||||||
if(err) *err = "Texture id out of remap range";
|
if(err) *err = "Идентификатор текстуры вне диапазона переназначения";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32_t newId = remap[src.TexId];
|
uint32_t newId = remap[src.TexId];
|
||||||
@@ -733,7 +733,7 @@ private:
|
|||||||
explicit VM(TextureProvider provider) : Provider_(std::move(provider)) {}
|
explicit VM(TextureProvider provider) : Provider_(std::move(provider)) {}
|
||||||
|
|
||||||
bool run(const std::vector<uint8_t>& code, OwnedTexture& out, double timeSeconds, std::string* err) {
|
bool run(const std::vector<uint8_t>& code, OwnedTexture& out, double timeSeconds, std::string* err) {
|
||||||
if(code.empty()) { if(err) *err="Empty bytecode"; return false; }
|
if(code.empty()) { if(err) *err="Пустой байткод"; return false; }
|
||||||
|
|
||||||
Image cur;
|
Image cur;
|
||||||
std::unordered_map<uint32_t, Image> texCache;
|
std::unordered_map<uint32_t, Image> texCache;
|
||||||
@@ -742,7 +742,7 @@ private:
|
|||||||
size_t ip = 0;
|
size_t ip = 0;
|
||||||
|
|
||||||
auto need = [&](size_t n)->bool{
|
auto need = [&](size_t n)->bool{
|
||||||
if(ip + n > code.size()) { if(err) *err="Bytecode truncated"; return false; }
|
if(ip + n > code.size()) { if(err) *err="Байткод усечен"; return false; }
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -997,7 +997,7 @@ private:
|
|||||||
|
|
||||||
auto t = Provider_(id);
|
auto t = Provider_(id);
|
||||||
if(!t || !t->Pixels || !t->Width || !t->Height) {
|
if(!t || !t->Pixels || !t->Width || !t->Height) {
|
||||||
if(err) *err = "Texture id not found: " + std::to_string(id);
|
if(err) *err = "Идентификатор текстуры не найден: " + std::to_string(id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
Image img;
|
Image img;
|
||||||
@@ -1019,7 +1019,7 @@ private:
|
|||||||
|
|
||||||
size_t start = size_t(off);
|
size_t start = size_t(off);
|
||||||
size_t end = start + size_t(len);
|
size_t end = start + size_t(len);
|
||||||
if(end > code.size()) { if(err) *err="Subprogram out of range"; return {}; }
|
if(end > code.size()) { if(err) *err="Подпрограмма выходит за пределы"; return {}; }
|
||||||
|
|
||||||
std::vector<uint8_t> slice(code.begin()+start, code.begin()+end);
|
std::vector<uint8_t> slice(code.begin()+start, code.begin()+end);
|
||||||
OwnedTexture tmp;
|
OwnedTexture tmp;
|
||||||
@@ -1040,7 +1040,7 @@ private:
|
|||||||
std::string* err) {
|
std::string* err) {
|
||||||
if(src.Kind == SrcKind::TexId) return _loadTex(src.TexId24, texCache, err);
|
if(src.Kind == SrcKind::TexId) return _loadTex(src.TexId24, texCache, err);
|
||||||
if(src.Kind == SrcKind::Sub) return _loadSub(code, src.Off24, src.Len24, texCache, subCache, timeSeconds, err);
|
if(src.Kind == SrcKind::Sub) return _loadSub(code, src.Off24, src.Len24, texCache, subCache, timeSeconds, err);
|
||||||
if(err) *err = "Unknown SrcKind";
|
if(err) *err = "Неизвестный SrcKind";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1307,8 +1307,10 @@ private:
|
|||||||
// ========================
|
// ========================
|
||||||
// Minimal DSL Lexer/Parser
|
// Minimal DSL Lexer/Parser
|
||||||
// now supports:
|
// now supports:
|
||||||
|
// name |> op(...)
|
||||||
|
// 32x32 "#RRGGBBAA"
|
||||||
|
// optional prefix:
|
||||||
// tex name |> op(...)
|
// tex name |> op(...)
|
||||||
// tex 32x32 "#RRGGBBAA"
|
|
||||||
// nested only where op expects a texture arg:
|
// nested only where op expects a texture arg:
|
||||||
// overlay( tex other |> ... )
|
// overlay( tex other |> ... )
|
||||||
// Also supports overlay(other) / mask(other) / lowpart(50, other)
|
// Also supports overlay(other) / mask(other) / lowpart(50, other)
|
||||||
@@ -1501,19 +1503,20 @@ private:
|
|||||||
bool _parseProgram(std::string* err) {
|
bool _parseProgram(std::string* err) {
|
||||||
Lexer lx{Source_};
|
Lexer lx{Source_};
|
||||||
Tok t = lx.next();
|
Tok t = lx.next();
|
||||||
if(!(t.Kind==TokKind::Ident && t.Text=="tex")) {
|
bool hasTexPrefix = (t.Kind == TokKind::Ident && t.Text == "tex");
|
||||||
if(err) *err="Expected 'tex' at start";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile base into main Code_
|
// compile base into main Code_
|
||||||
if(!_compileBaseAfterTex(lx, Code_, /*abs*/&Patches_, /*rel*/nullptr, err)) return false;
|
if(hasTexPrefix) {
|
||||||
|
if(!_compileBaseAfterTex(lx, Code_, /*abs*/&Patches_, /*rel*/nullptr, err)) return false;
|
||||||
|
} else {
|
||||||
|
if(!_compileBaseFromToken(lx, t, Code_, /*abs*/&Patches_, /*rel*/nullptr, err)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
// pipeline: |> op ...
|
// pipeline: |> op ...
|
||||||
Tok nt = lx.next();
|
Tok nt = lx.next();
|
||||||
while (nt.Kind == TokKind::Pipe) {
|
while (nt.Kind == TokKind::Pipe) {
|
||||||
Tok opName = lx.next();
|
Tok opName = lx.next();
|
||||||
if(opName.Kind != TokKind::Ident) { if(err) *err="Expected op name after |>"; return false; }
|
if(opName.Kind != TokKind::Ident) { if(err) *err="Ожидалось имя операции после |>"; return false; }
|
||||||
ParsedOp op; op.Name = opName.Text;
|
ParsedOp op; op.Name = opName.Text;
|
||||||
|
|
||||||
Tok peek = lx.next();
|
Tok peek = lx.next();
|
||||||
@@ -1530,7 +1533,7 @@ private:
|
|||||||
_emitOp(Code_, Op::End);
|
_emitOp(Code_, Op::End);
|
||||||
if (Code_.size() > MaxCodeBytes) {
|
if (Code_.size() > MaxCodeBytes) {
|
||||||
if (err)
|
if (err)
|
||||||
*err = "Pipeline bytecode too large: " + std::to_string(Code_.size()) +
|
*err = "Байткод пайплайна слишком большой: " + std::to_string(Code_.size()) +
|
||||||
" > MaxCodeBytes(" + std::to_string(MaxCodeBytes) + ")";
|
" > MaxCodeBytes(" + std::to_string(MaxCodeBytes) + ")";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1539,12 +1542,13 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========================
|
// ========================
|
||||||
// Base compilation after 'tex'
|
// Base compilation (optionally after 'tex')
|
||||||
// supports:
|
// supports:
|
||||||
// 1) tex name
|
// 1) name
|
||||||
// 2) tex "name(.png/.jpg/.jpeg)" (allowed but normalized)
|
// 2) "name(.png/.jpg/.jpeg)" (allowed but normalized)
|
||||||
// 3) tex anim(...)
|
// 3) anim(...)
|
||||||
// 4) tex 32x32 "#RRGGBBAA"
|
// 4) 32x32 "#RRGGBBAA"
|
||||||
|
// optional: all of the above may be prefixed with 'tex'
|
||||||
// ========================
|
// ========================
|
||||||
bool _compileBaseAfterTex(Lexer& lx,
|
bool _compileBaseAfterTex(Lexer& lx,
|
||||||
std::vector<uint8_t>& out,
|
std::vector<uint8_t>& out,
|
||||||
@@ -1552,10 +1556,23 @@ private:
|
|||||||
std::vector<RelPatch>* relPatches,
|
std::vector<RelPatch>* relPatches,
|
||||||
std::string* err) {
|
std::string* err) {
|
||||||
Tok a = lx.next();
|
Tok a = lx.next();
|
||||||
|
return _compileBaseFromToken(lx, a, out, absPatches, relPatches, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _compileBaseFromToken(Lexer& lx,
|
||||||
|
const Tok& a,
|
||||||
|
std::vector<uint8_t>& out,
|
||||||
|
std::vector<Patch>* absPatches,
|
||||||
|
std::vector<RelPatch>* relPatches,
|
||||||
|
std::string* err) {
|
||||||
|
if(a.Kind == TokKind::End) {
|
||||||
|
if(err) *err="Ожидалось текстурное выражение";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(a.Kind == TokKind::Ident && a.Text == "anim") {
|
if(a.Kind == TokKind::Ident && a.Text == "anim") {
|
||||||
Tok lp = lx.next();
|
Tok lp = lx.next();
|
||||||
if(lp.Kind != TokKind::LParen) { if(err) *err="Expected '(' after anim"; return false; }
|
if(lp.Kind != TokKind::LParen) { if(err) *err="Ожидалась '(' после anim"; return false; }
|
||||||
|
|
||||||
ParsedOp op; op.Name="anim";
|
ParsedOp op; op.Name="anim";
|
||||||
if(!_parseArgList(lx, op, err)) return false;
|
if(!_parseArgList(lx, op, err)) return false;
|
||||||
@@ -1581,7 +1598,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::string tex = namedS("tex").value_or(posS(0).value_or(""));
|
std::string tex = namedS("tex").value_or(posS(0).value_or(""));
|
||||||
if(tex.empty()) { if(err) *err="anim requires texture name"; return false; }
|
if(tex.empty()) { if(err) *err="anim требует имя текстуры"; return false; }
|
||||||
|
|
||||||
uint32_t frameW = namedU("frame_w").value_or(namedU("w").value_or(posU(1).value_or(0)));
|
uint32_t frameW = namedU("frame_w").value_or(namedU("w").value_or(posU(1).value_or(0)));
|
||||||
uint32_t frameH = namedU("frame_h").value_or(namedU("h").value_or(posU(2).value_or(0)));
|
uint32_t frameH = namedU("frame_h").value_or(namedU("h").value_or(posU(2).value_or(0)));
|
||||||
@@ -1593,7 +1610,7 @@ private:
|
|||||||
bool horizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h'));
|
bool horizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h'));
|
||||||
|
|
||||||
if(frameW > 65535u || frameH > 65535u || frames > 65535u) {
|
if(frameW > 65535u || frameH > 65535u || frames > 65535u) {
|
||||||
if(err) *err="anim params must fit uint16";
|
if(err) *err="параметры anim должны помещаться в uint16";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1611,28 +1628,28 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(a.Kind == TokKind::Ident || a.Kind == TokKind::String) {
|
if(a.Kind == TokKind::Ident || a.Kind == TokKind::String) {
|
||||||
// tex name (or tex "name.png" => normalized)
|
// name (or "name.png" => normalized)
|
||||||
_emitOp(out, Op::Base_Tex);
|
_emitOp(out, Op::Base_Tex);
|
||||||
_emitSrcTexName(out, absPatches, relPatches, a.Text);
|
_emitSrcTexName(out, absPatches, relPatches, a.Text);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(a.Kind == TokKind::Number) {
|
if(a.Kind == TokKind::Number) {
|
||||||
// tex 32x32 "#RRGGBBAA"
|
// 32x32 "#RRGGBBAA"
|
||||||
Tok xTok = lx.next();
|
Tok xTok = lx.next();
|
||||||
Tok b = lx.next();
|
Tok b = lx.next();
|
||||||
Tok colTok = lx.next();
|
Tok colTok = lx.next();
|
||||||
if(xTok.Kind != TokKind::X || b.Kind != TokKind::Number || (colTok.Kind!=TokKind::Ident && colTok.Kind!=TokKind::String)) {
|
if(xTok.Kind != TokKind::X || b.Kind != TokKind::Number || (colTok.Kind!=TokKind::Ident && colTok.Kind!=TokKind::String)) {
|
||||||
if(err) *err="Expected: tex <w>x<h> <#color>";
|
if(err) *err="Ожидалось: <w>x<h> <#color>";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32_t w = a.U32, h = b.U32;
|
uint32_t w = a.U32, h = b.U32;
|
||||||
uint32_t color = 0;
|
uint32_t color = 0;
|
||||||
if(!_parseHexColor(colTok.Text, color)) {
|
if(!_parseHexColor(colTok.Text, color)) {
|
||||||
if(err) *err="Bad color literal (use #RRGGBB or #RRGGBBAA)";
|
if(err) *err="Неверный литерал цвета (используйте #RRGGBB или #RRGGBBAA)";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(w>65535u || h>65535u) { if(err) *err="w/h must fit in uint16"; return false; }
|
if(w>65535u || h>65535u) { if(err) *err="w/h должны помещаться в uint16"; return false; }
|
||||||
_emitOp(out, Op::Base_Fill);
|
_emitOp(out, Op::Base_Fill);
|
||||||
_emitU16(out, w);
|
_emitU16(out, w);
|
||||||
_emitU16(out, h);
|
_emitU16(out, h);
|
||||||
@@ -1640,7 +1657,7 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(err) *err="Bad 'tex' base expression";
|
if(err) *err="Некорректное базовое текстурное выражение";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1661,7 +1678,7 @@ private:
|
|||||||
if(!_compileSubProgramFromAlreadySawTex(lx, sub, err)) return false;
|
if(!_compileSubProgramFromAlreadySawTex(lx, sub, err)) return false;
|
||||||
|
|
||||||
Tok end = lx.next();
|
Tok end = lx.next();
|
||||||
if(end.Kind != TokKind::RParen) { if(err) *err="Expected ')' after sub texture expr"; return false; }
|
if(end.Kind != TokKind::RParen) { if(err) *err="Ожидалась ')' после подвыражения текстуры"; return false; }
|
||||||
|
|
||||||
PendingSub_[&op] = std::move(sub);
|
PendingSub_[&op] = std::move(sub);
|
||||||
return true;
|
return true;
|
||||||
@@ -1695,7 +1712,7 @@ private:
|
|||||||
if(t.Kind == TokKind::Comma) { t = lx.next(); continue; }
|
if(t.Kind == TokKind::Comma) { t = lx.next(); continue; }
|
||||||
if(t.Kind == TokKind::RParen) return true;
|
if(t.Kind == TokKind::RParen) return true;
|
||||||
|
|
||||||
if(err) *err = "Expected ',' or ')' in argument list";
|
if(err) *err = "Ожидалась ',' или ')' в списке аргументов";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1728,7 +1745,7 @@ private:
|
|||||||
if(t.Kind == TokKind::Comma) { t = lx.next(); continue; }
|
if(t.Kind == TokKind::Comma) { t = lx.next(); continue; }
|
||||||
if(t.Kind == TokKind::RParen) return true;
|
if(t.Kind == TokKind::RParen) return true;
|
||||||
|
|
||||||
if(err) *err = "Expected ',' or ')' in argument list";
|
if(err) *err = "Ожидалась ',' или ')' в списке аргументов";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1737,7 +1754,7 @@ private:
|
|||||||
if(t.Kind == TokKind::Number) { out.Kind=ArgVal::ValueKind::U32; out.U32=t.U32; return true; }
|
if(t.Kind == TokKind::Number) { out.Kind=ArgVal::ValueKind::U32; out.U32=t.U32; return true; }
|
||||||
if(t.Kind == TokKind::String) { out.Kind=ArgVal::ValueKind::Str; out.S=t.Text; return true; }
|
if(t.Kind == TokKind::String) { out.Kind=ArgVal::ValueKind::Str; out.S=t.Text; return true; }
|
||||||
if(t.Kind == TokKind::Ident) { out.Kind=ArgVal::ValueKind::Ident; out.S=t.Text; return true; }
|
if(t.Kind == TokKind::Ident) { out.Kind=ArgVal::ValueKind::Ident; out.S=t.Text; return true; }
|
||||||
if(err) *err = "Expected value token";
|
if(err) *err = "Ожидалось значение";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1764,12 +1781,12 @@ private:
|
|||||||
// peek
|
// peek
|
||||||
Tok nt = lx.peek();
|
Tok nt = lx.peek();
|
||||||
if(nt.Kind == TokKind::RParen) break;
|
if(nt.Kind == TokKind::RParen) break;
|
||||||
if(nt.Kind != TokKind::Pipe) { if(err) *err="Sub tex: expected '|>' or ')'"; return false; }
|
if(nt.Kind != TokKind::Pipe) { if(err) *err="Подтекстура: ожидалось '|>' или ')'"; return false; }
|
||||||
|
|
||||||
// consume pipe
|
// consume pipe
|
||||||
lx.next();
|
lx.next();
|
||||||
Tok opName = lx.next();
|
Tok opName = lx.next();
|
||||||
if(opName.Kind != TokKind::Ident) { if(err) *err="Sub tex: expected op name"; return false; }
|
if(opName.Kind != TokKind::Ident) { if(err) *err="Подтекстура: ожидалось имя операции"; return false; }
|
||||||
|
|
||||||
ParsedOp op; op.Name = opName.Text;
|
ParsedOp op; op.Name = opName.Text;
|
||||||
|
|
||||||
@@ -1783,7 +1800,7 @@ private:
|
|||||||
// Instead: enforce parentheses for ops in subprogram except no-arg ops (brighten/noalpha) which can be without.
|
// Instead: enforce parentheses for ops in subprogram except no-arg ops (brighten/noalpha) which can be without.
|
||||||
// To keep behavior identical to main, we handle no-arg by rewinding I one token is not possible,
|
// To keep behavior identical to main, we handle no-arg by rewinding I one token is not possible,
|
||||||
// so we accept that in subprogram, no-arg ops must be written as brighten() etc.
|
// so we accept that in subprogram, no-arg ops must be written as brighten() etc.
|
||||||
if(err) *err="Sub tex: no-arg ops must use parentheses, e.g. brighten()";
|
if(err) *err="Подтекстура: операции без аргументов должны использовать скобки, напр. brighten()";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1795,13 +1812,13 @@ private:
|
|||||||
while (true) {
|
while (true) {
|
||||||
Tok nt = lx.peek();
|
Tok nt = lx.peek();
|
||||||
if(nt.Kind == TokKind::RParen) break;
|
if(nt.Kind == TokKind::RParen) break;
|
||||||
if(nt.Kind != TokKind::Pipe) { if(err) *err="Sub tex: expected '|>' or ')'"; return false; }
|
if(nt.Kind != TokKind::Pipe) { if(err) *err="Подтекстура: ожидалось '|>' или ')'"; return false; }
|
||||||
|
|
||||||
// consume pipe
|
// consume pipe
|
||||||
lx.next();
|
lx.next();
|
||||||
|
|
||||||
Tok opName = lx.next();
|
Tok opName = lx.next();
|
||||||
if(opName.Kind != TokKind::Ident) { if(err) *err="Sub tex: expected op name"; return false; }
|
if(opName.Kind != TokKind::Ident) { if(err) *err="Подтекстура: ожидалось имя операции"; return false; }
|
||||||
|
|
||||||
ParsedOp op; op.Name = opName.Text;
|
ParsedOp op; op.Name = opName.Text;
|
||||||
|
|
||||||
@@ -1838,12 +1855,12 @@ private:
|
|||||||
const size_t len = sub.Bytes.size();
|
const size_t len = sub.Bytes.size();
|
||||||
|
|
||||||
if(offset > 0xFFFFFFu || len > 0xFFFFFFu || (offset + len) > 0xFFFFFFu) {
|
if(offset > 0xFFFFFFu || len > 0xFFFFFFu || (offset + len) > 0xFFFFFFu) {
|
||||||
if(err) *err = "Subprogram слишком большой (off/len должны влезать в u24 байт)";
|
if(err) *err = "Подпрограмма слишком большая (off/len должны влезать в u24 байт)";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(offset + len > MaxCodeBytes) {
|
if(offset + len > MaxCodeBytes) {
|
||||||
if(err) *err = "Pipeline bytecode too large after sub append: " +
|
if(err) *err = "Байткод пайплайна слишком большой после вставки подпрограммы: " +
|
||||||
std::to_string(offset + len) + " > MaxCodeBytes(" + std::to_string(MaxCodeBytes) + ")";
|
std::to_string(offset + len) + " > MaxCodeBytes(" + std::to_string(MaxCodeBytes) + ")";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1904,7 +1921,7 @@ private:
|
|||||||
|
|
||||||
auto emitSrcFromPendingSub = [&]()->bool{
|
auto emitSrcFromPendingSub = [&]()->bool{
|
||||||
auto it = PendingSub_.find(&op);
|
auto it = PendingSub_.find(&op);
|
||||||
if(it == PendingSub_.end()) { if(err) *err="Internal: missing subprogram"; return false; }
|
if(it == PendingSub_.end()) { if(err) *err="Внутренняя ошибка: отсутствует подпрограмма"; return false; }
|
||||||
uint32_t off=0, len=0;
|
uint32_t off=0, len=0;
|
||||||
if(!_appendSubprogram(out, std::move(it->second), absPatches, relPatches, off, len, err)) return false;
|
if(!_appendSubprogram(out, std::move(it->second), absPatches, relPatches, off, len, err)) return false;
|
||||||
PendingSub_.erase(it);
|
PendingSub_.erase(it);
|
||||||
@@ -1919,7 +1936,7 @@ private:
|
|||||||
|
|
||||||
// allow overlay(name) or overlay(tex=name)
|
// allow overlay(name) or overlay(tex=name)
|
||||||
std::string tex = namedS("tex").value_or(posS(0).value_or(""));
|
std::string tex = namedS("tex").value_or(posS(0).value_or(""));
|
||||||
if(tex.empty()) { if(err) *err="overlay requires texture arg"; return false; }
|
if(tex.empty()) { if(err) *err="overlay требует аргумент-текстуру"; return false; }
|
||||||
emitSrcFromName(tex);
|
emitSrcFromName(tex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1929,14 +1946,14 @@ private:
|
|||||||
if(!op.Pos.empty() && op.Pos[0].S == "__SUBTEX__") return emitSrcFromPendingSub();
|
if(!op.Pos.empty() && op.Pos[0].S == "__SUBTEX__") return emitSrcFromPendingSub();
|
||||||
|
|
||||||
std::string tex = namedS("tex").value_or(posS(0).value_or(""));
|
std::string tex = namedS("tex").value_or(posS(0).value_or(""));
|
||||||
if(tex.empty()) { if(err) *err="mask requires texture arg"; return false; }
|
if(tex.empty()) { if(err) *err="mask требует аргумент-текстуру"; return false; }
|
||||||
emitSrcFromName(tex);
|
emitSrcFromName(tex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(op.Name == "lowpart") {
|
if(op.Name == "lowpart") {
|
||||||
uint32_t pct = namedU("percent").value_or(posU(0).value_or(0));
|
uint32_t pct = namedU("percent").value_or(posU(0).value_or(0));
|
||||||
if(!pct) { if(err) *err="lowpart requires percent"; return false; }
|
if(!pct) { if(err) *err="lowpart требует процент"; return false; }
|
||||||
|
|
||||||
_emitOp(out, Op::LowPart);
|
_emitOp(out, Op::LowPart);
|
||||||
_emitU8(out, std::min<uint32_t>(100u, pct));
|
_emitU8(out, std::min<uint32_t>(100u, pct));
|
||||||
@@ -1945,7 +1962,7 @@ private:
|
|||||||
if(op.Pos.size() >= 2 && op.Pos[1].S == "__SUBTEX__") return emitSrcFromPendingSub();
|
if(op.Pos.size() >= 2 && op.Pos[1].S == "__SUBTEX__") return emitSrcFromPendingSub();
|
||||||
|
|
||||||
std::string tex = namedS("tex").value_or(posS(1).value_or(""));
|
std::string tex = namedS("tex").value_or(posS(1).value_or(""));
|
||||||
if(tex.empty()) { if(err) *err="lowpart requires texture arg"; return false; }
|
if(tex.empty()) { if(err) *err="lowpart требует аргумент-текстуру"; return false; }
|
||||||
emitSrcFromName(tex);
|
emitSrcFromName(tex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1954,7 +1971,7 @@ private:
|
|||||||
if(op.Name == "resize") {
|
if(op.Name == "resize") {
|
||||||
uint32_t w = namedU("w").value_or(posU(0).value_or(0));
|
uint32_t w = namedU("w").value_or(posU(0).value_or(0));
|
||||||
uint32_t h = namedU("h").value_or(posU(1).value_or(0));
|
uint32_t h = namedU("h").value_or(posU(1).value_or(0));
|
||||||
if(!w || !h || w>65535u || h>65535u) { if(err) *err="resize(w,h) must fit uint16"; return false; }
|
if(!w || !h || w>65535u || h>65535u) { if(err) *err="resize(w,h) должны помещаться в uint16"; return false; }
|
||||||
_emitOp(out, Op::Resize); _emitU16(out, w); _emitU16(out, h);
|
_emitOp(out, Op::Resize); _emitU16(out, w); _emitU16(out, h);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1979,7 +1996,7 @@ private:
|
|||||||
if(op.Name == "make_alpha") {
|
if(op.Name == "make_alpha") {
|
||||||
std::string col = namedS("color").value_or(posS(0).value_or(""));
|
std::string col = namedS("color").value_or(posS(0).value_or(""));
|
||||||
uint32_t argb=0;
|
uint32_t argb=0;
|
||||||
if(!_parseHexColor(col, argb)) { if(err) *err="make_alpha requires color #RRGGBB"; return false; }
|
if(!_parseHexColor(col, argb)) { if(err) *err="make_alpha требует цвет #RRGGBB"; return false; }
|
||||||
uint32_t rgb24 = argb & 0x00FFFFFFu;
|
uint32_t rgb24 = argb & 0x00FFFFFFu;
|
||||||
_emitOp(out, Op::MakeAlpha);
|
_emitOp(out, Op::MakeAlpha);
|
||||||
_emitU8(out, (rgb24 >> 16) & 0xFFu);
|
_emitU8(out, (rgb24 >> 16) & 0xFFu);
|
||||||
@@ -2020,7 +2037,7 @@ private:
|
|||||||
auto compileColorOp = [&](Op opcode, bool needsRatio)->bool{
|
auto compileColorOp = [&](Op opcode, bool needsRatio)->bool{
|
||||||
std::string col = namedS("color").value_or(posS(0).value_or(""));
|
std::string col = namedS("color").value_or(posS(0).value_or(""));
|
||||||
uint32_t argb=0;
|
uint32_t argb=0;
|
||||||
if(!_parseHexColor(col, argb)) { if(err) *err="Bad color literal"; return false; }
|
if(!_parseHexColor(col, argb)) { if(err) *err="Неверный литерал цвета"; return false; }
|
||||||
_emitOp(out, opcode);
|
_emitOp(out, opcode);
|
||||||
_emitU32(out, argb);
|
_emitU32(out, argb);
|
||||||
if(needsRatio) {
|
if(needsRatio) {
|
||||||
@@ -2045,7 +2062,7 @@ private:
|
|||||||
bool horizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h'));
|
bool horizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h'));
|
||||||
|
|
||||||
if(frameW > 65535u || frameH > 65535u || frames > 65535u) {
|
if(frameW > 65535u || frameH > 65535u || frames > 65535u) {
|
||||||
if(err) *err="anim params must fit uint16";
|
if(err) *err="параметры anim должны помещаться в uint16";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2061,7 +2078,7 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(err) *err = "Unknown op: " + op.Name;
|
if(err) *err = "Неизвестная операция: " + op.Name;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user