From 36b9864183643bb0dca8964d3618870e728bbbb9 Mon Sep 17 00:00:00 2001 From: cueki Date: Thu, 24 Sep 2026 18:23:02 -0700 Subject: [PATCH 1/3] kvpp: skip UTF-8 BOM --- include/kvpp/KV1.h | 4 ++++ include/kvpp/KV1Writer.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/kvpp/KV1.h b/include/kvpp/KV1.h index fd0020937..4957f7d22 100644 --- a/include/kvpp/KV1.h +++ b/include/kvpp/KV1.h @@ -218,6 +218,10 @@ class KV1 : public KV1ElementReadable { explicit KV1(std::string_view kv1Data, bool useEscapeSequences_ = false) : KV1ElementReadable() , useEscapeSequences(useEscapeSequences_) { + // Skip the UTF-8 BOM + if (kv1Data.starts_with("\xEF\xBB\xBF")) { + kv1Data.remove_prefix(3); + } if (kv1Data.empty()) { return; } diff --git a/include/kvpp/KV1Writer.h b/include/kvpp/KV1Writer.h index 080b76a70..48e9891d2 100644 --- a/include/kvpp/KV1Writer.h +++ b/include/kvpp/KV1Writer.h @@ -349,6 +349,10 @@ class KV1Writer : public KV1ElementWritable { explicit KV1Writer(std::string_view kv1Data = "", bool useEscapeSequences_ = false) : KV1ElementWritable() , useEscapeSequences(useEscapeSequences_) { + // Skip the UTF-8 BOM + if (kv1Data.starts_with("\xEF\xBB\xBF")) { + kv1Data.remove_prefix(3); + } if (kv1Data.empty()) { return; } From 009da29d14b12fe535cc19a9fe3b4ccafb6b9ca8 Mon Sep 17 00:00:00 2001 From: cueki Date: Thu, 24 Sep 2026 18:24:40 -0700 Subject: [PATCH 2/3] python: fix instance leak in constructors --- lang/python/src/kvpp.h | 4 ++-- lang/python/src/vtfpp.h | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lang/python/src/kvpp.h b/lang/python/src/kvpp.h index 489e8623c..9eea5358f 100644 --- a/lang/python/src/kvpp.h +++ b/lang/python/src/kvpp.h @@ -271,7 +271,7 @@ inline void register_python(py::module_& m) { cDMX .def(py::init(), "encoding_type"_a, "encoding_version"_a, "format_type"_a, "format_version"_a) .def("__init__", [](DMX* self, const py::bytes& dmxData) { - return new(self) DMX{std::span{static_cast(dmxData.data()), dmxData.size()}}; + new(self) DMX{std::span{static_cast(dmxData.data()), dmxData.size()}}; }, "dmx_data"_a) .def(py::init(), "dmx_data"_a) .def("__bool__", &DMX::operator bool) @@ -415,7 +415,7 @@ inline void register_python(py::module_& m) { py::class_(kvpp, "KV1Binary") .def("__init__", [](KV1Binary* self, const py::bytes& kv1Data, bool use64BitPointers = true) { - return new(self) KV1Binary{std::span{static_cast(kv1Data.data()), kv1Data.size()}, use64BitPointers}; + new(self) KV1Binary{std::span{static_cast(kv1Data.data()), kv1Data.size()}, use64BitPointers}; }, "kv1_data"_a, "use_64_bit_pointers"_a = true) .def("bake", [](const KV1Binary& self) { const auto d = self.bake(); diff --git a/lang/python/src/vtfpp.h b/lang/python/src/vtfpp.h index d9180c052..ea3b173ed 100644 --- a/lang/python/src/vtfpp.h +++ b/lang/python/src/vtfpp.h @@ -35,7 +35,7 @@ inline void register_python(py::module_& m) { cHOT .def(py::init()) .def("__init__", [](HOT* self, const py::bytes& hotData) { - return new(self) HOT{{static_cast(hotData.data()), hotData.size()}}; + new(self) HOT{{static_cast(hotData.data()), hotData.size()}}; }, "hot_data"_a) .def(py::init(), "hot_path"_a) .def("__bool__", &HOT::operator bool, py::is_operator()) @@ -369,7 +369,7 @@ inline void register_python(py::module_& m) { cPPL .def(py::init(), "model_checksum"_a, "format"_a = ImageFormat::RGB888, "version"_a = 0) .def("__init__", [](PPL* self, const py::bytes& pplData) { - return new(self) PPL{{static_cast(pplData.data()), pplData.size()}}; + new(self) PPL{{static_cast(pplData.data()), pplData.size()}}; }, "ppl_data"_a) .def(py::init(), "path"_a) .def("__bool__", &PPL::operator bool, py::is_operator()) @@ -409,7 +409,7 @@ inline void register_python(py::module_& m) { py::class_(vtfpp, "PSFrames") .def("__init__", [](PSFrames* self, const py::bytes& psFramesData) { - return new(self) PSFrames{std::span{static_cast(psFramesData.data()), psFramesData.size()}}; + new(self) PSFrames{std::span{static_cast(psFramesData.data()), psFramesData.size()}}; }, "ps_frames_data"_a) .def(py::init(), "ps_frames_path"_a) .def("__bool__", &PSFrames::operator bool, py::is_operator()) @@ -462,7 +462,7 @@ inline void register_python(py::module_& m) { cSHT .def(py::init()) .def("__init__", [](SHT* self, const py::bytes& shtData) { - return new(self) SHT{{static_cast(shtData.data()), shtData.size()}}; + new(self) SHT{{static_cast(shtData.data()), shtData.size()}}; }, "sht_data"_a) .def(py::init(), "sht_path"_a) .def("__bool__", &SHT::operator bool, py::is_operator()) @@ -483,7 +483,7 @@ inline void register_python(py::module_& m) { py::class_(vtfpp, "TTX") .def(py::init(), "vtf"_a) .def("__init__", [](TTX* self, const py::bytes& tthData, const py::bytes& ttzData) { - return new(self) TTX{{static_cast(tthData.data()), tthData.size()}, {static_cast(ttzData.data()), ttzData.size()}}; + new(self) TTX{{static_cast(tthData.data()), tthData.size()}, {static_cast(ttzData.data()), ttzData.size()}}; }, "tth_data"_a, "ttz_data"_a) .def(py::init(), "tth_path"_a, "ttz_path"_a) .def("__bool__", &TTX::operator bool, py::is_operator()) @@ -525,7 +525,7 @@ inline void register_python(py::module_& m) { cVBF .def("__init__", [](VBF* self, const py::bytes& vbfData) { - return new(self) VBF{std::span{static_cast(vbfData.data()), vbfData.size()}}; + new(self) VBF{std::span{static_cast(vbfData.data()), vbfData.size()}}; }, "vbf_data"_a) .def(py::init(), "vbf_path"_a) .def("__bool__", &VBF::operator bool) @@ -697,7 +697,7 @@ inline void register_python(py::module_& m) { .def_ro_static("FORMAT_DEFAULT", &VTF::FORMAT_DEFAULT) .def(py::init()) .def("__init__", [](VTF* self, const py::bytes& vtfData, bool parseHeaderOnly = false, bool hdr = false) { - return new(self) VTF{std::span{static_cast(vtfData.data()), vtfData.size()}, parseHeaderOnly, hdr}; + new(self) VTF{std::span{static_cast(vtfData.data()), vtfData.size()}, parseHeaderOnly, hdr}; }, "vtf_data"_a, "parse_header_only"_a = false, "hdr"_a = false) .def(py::init(), "vtf_path"_a, "parse_header_only"_a = false) .def("__bool__", &VTF::operator bool, py::is_operator()) From 662bbdfcf69c9b5d7af922bc5c3b5f12a6fd592c Mon Sep 17 00:00:00 2001 From: cueki Date: Thu, 24 Sep 2026 18:24:55 -0700 Subject: [PATCH 3/3] python: add missing default arguments to kvpp bindings --- lang/python/src/kvpp.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lang/python/src/kvpp.h b/lang/python/src/kvpp.h index 9eea5358f..dc13d0f51 100644 --- a/lang/python/src/kvpp.h +++ b/lang/python/src/kvpp.h @@ -238,7 +238,7 @@ inline void register_python(py::module_& m) { .def_prop_rw("uuid", &DMXElement::getUUID, &DMXElement::setUUID) .def("has_attribute", &DMXElement::hasAttribute, "attribute_key"_a) .def("__contains__", &DMXElement::hasAttribute, "attribute_key"_a) - .def("add_attribute", &DMXElement::addAttribute, "key"_a, "value"_a = DMXValue::Generic{}, py::rv_policy::reference_internal) + .def("add_attribute", &DMXElement::addAttribute, "key"_a, "value"_a.none() = DMXValue::Generic{}, py::rv_policy::reference_internal) .def("get_attribute_count", py::overload_cast<>(&DMXElement::getAttributeCount, py::const_)) .def("__len__", py::overload_cast<>(&DMXElement::getAttributeCount, py::const_)) .def("get_attribute_count_with_key", py::overload_cast(&DMXElement::getAttributeCount, py::const_)) @@ -248,7 +248,7 @@ inline void register_python(py::module_& m) { }, py::keep_alive<0, 1>()) .def("__getitem__", py::overload_cast(&DMXElement::operator[]), "n"_a, py::rv_policy::reference_internal) .def("__getitem__", py::overload_cast(&DMXElement::operator[]), "attribute_key"_a, py::rv_policy::reference_internal) - .def("get_child", py::overload_cast(&DMXElement::operator()), "attribute_key"_a, "n"_a, py::rv_policy::reference_internal) + .def("get_child", py::overload_cast(&DMXElement::operator()), "attribute_key"_a, "n"_a = 0, py::rv_policy::reference_internal) .def("remove_child", py::overload_cast(&DMXElement::removeAttribute), "n"_a) .def("__delitem__", py::overload_cast(&DMXElement::removeAttribute), "n"_a) .def("remove_child", py::overload_cast(&DMXElement::removeAttribute), "attribute_key"_a, "n"_a = -1) @@ -296,7 +296,7 @@ inline void register_python(py::module_& m) { }, py::keep_alive<0, 1>()) .def("__getitem__", py::overload_cast(&DMX::operator[]), "n"_a, py::rv_policy::reference_internal) .def("__getitem__", py::overload_cast(&DMX::operator[]), "key"_a, py::rv_policy::reference_internal) - .def("get_child", py::overload_cast(&DMX::operator()), "key"_a, "n"_a, py::rv_policy::reference_internal) + .def("get_child", py::overload_cast(&DMX::operator()), "key"_a, "n"_a = 0, py::rv_policy::reference_internal) .def("remove_child", py::overload_cast(&DMX::removeElement), "n"_a) .def("__delitem__", py::overload_cast(&DMX::removeElement), "n"_a) .def("bake", [](const DMX& self) { @@ -325,7 +325,7 @@ inline void register_python(py::module_& m) { }, py::keep_alive<0, 1>()) .def("__getitem__", py::overload_cast(&KV1ElementReadable<>::operator[], py::const_), "n"_a, py::rv_policy::reference_internal) .def("__getitem__", py::overload_cast(&KV1ElementReadable<>::operator[], py::const_), "child_key"_a, py::rv_policy::reference_internal) - .def("get_child", py::overload_cast(&KV1ElementReadable<>::operator(), py::const_), "child_key"_a, "n"_a, py::rv_policy::reference_internal) + .def("get_child", py::overload_cast(&KV1ElementReadable<>::operator(), py::const_), "child_key"_a, "n"_a = 0, py::rv_policy::reference_internal) .def("is_invalid", &KV1ElementReadable<>::isInvalid) .def("get_invalid", &KV1ElementReadable<>::getInvalid) .def("__bool__", &KV1ElementReadable<>::operator bool); @@ -351,7 +351,7 @@ inline void register_python(py::module_& m) { }, py::keep_alive<0, 1>()) .def("__getitem__", py::overload_cast(&KV1ElementWritable<>::operator[]), "n"_a, py::rv_policy::reference_internal) .def("__getitem__", py::overload_cast(&KV1ElementWritable<>::operator[]), "child_key"_a, py::rv_policy::reference_internal) - .def("get_child", py::overload_cast(&KV1ElementWritable<>::operator()), "child_key"_a, "n"_a, py::rv_policy::reference_internal) + .def("get_child", py::overload_cast(&KV1ElementWritable<>::operator()), "child_key"_a, "n"_a = 0, py::rv_policy::reference_internal) .def("remove_child", py::overload_cast(&KV1ElementWritable<>::removeChild), "n"_a) .def("__delitem__", py::overload_cast(&KV1ElementWritable<>::removeChild), "n"_a) .def("remove_child", py::overload_cast(&KV1ElementWritable<>::removeChild), "child_key"_a, "n"_a = -1) @@ -395,7 +395,7 @@ inline void register_python(py::module_& m) { .def("__contains__", &KV1BinaryElement::hasChild, "child_key"_a) .def("add_child", [](KV1BinaryElement& self, std::string_view key, KV1BinaryValue value = {}) -> KV1BinaryElement& { return self.addChild(key, std::move(value)); - }, "key"_a, "value"_a = KV1BinaryValue{}, py::rv_policy::reference_internal) + }, "key"_a, "value"_a.none() = KV1BinaryValue{}, py::rv_policy::reference_internal) .def_prop_ro("child_count", py::overload_cast<>(&KV1BinaryElement::getChildCount, py::const_)) .def("__len__", py::overload_cast<>(&KV1BinaryElement::getChildCount, py::const_)) .def("get_child_count_with_key", py::overload_cast(&KV1BinaryElement::getChildCount, py::const_), "child_key"_a) @@ -405,7 +405,7 @@ inline void register_python(py::module_& m) { }, py::keep_alive<0, 1>()) .def("__getitem__", py::overload_cast(&KV1BinaryElement::operator[]), "n"_a, py::rv_policy::reference_internal) .def("__getitem__", py::overload_cast(&KV1BinaryElement::operator[]), "child_key"_a, py::rv_policy::reference_internal) - .def("get_child", py::overload_cast(&KV1BinaryElement::operator()), "child_key"_a, "n"_a, py::rv_policy::reference_internal) + .def("get_child", py::overload_cast(&KV1BinaryElement::operator()), "child_key"_a, "n"_a = 0, py::rv_policy::reference_internal) .def("remove_child", py::overload_cast(&KV1BinaryElement::removeChild), "n"_a) .def("__delitem__", py::overload_cast(&KV1BinaryElement::removeChild), "n"_a) .def("remove_child", py::overload_cast(&KV1BinaryElement::removeChild), "child_key"_a, "n"_a = -1)