~quf/libcs3tio

6fa9106b5e71a579884ef892ffa9b619938e9489 — Lukas Himbert 9 months ago 4900a3d
t_notemons support
M 3rdparty/SenSchema => 3rdparty/SenSchema +1 -1
@@ 1,1 1,1 @@
Subproject commit 4f99cd63b211517a0241a616c8eef1589e104ae5
Subproject commit 20a68f3e6080eaf69b68457bef10af5dbba2fb88

M compile.sh => compile.sh +2 -2
@@ 6,7 6,7 @@ if test -z "$CXX"; then
  CXX=g++
fi

for x in base_list character cs3 cs3_tbl_entry element header_base header_extend master_quartz_base master_quartz_data master_quartz_dummy master_quartz_memo master_quartz_status orb_line_list quartz_cost slot_cost slot_ep; do
for x in base_list character cs3 cs3_tbl_entry element header_base header_extend master_quartz_base master_quartz_data master_quartz_dummy master_quartz_memo master_quartz_status orb_line_list quartz_cost qschapter qsmons slot_cost slot_ep; do
  "$CXX" -std=c++20 -W -Wall -Wno-unused-parameter -Werror -Wfatal-errors -fsanitize=address,undefined -I 3rdparty/kaitai_struct_cpp_stl_runtime/ -c ksp-src/$x.cpp -o build/obj-ksp/$x.o
done



@@ 19,7 19,7 @@ done
ar cr build/output/libcs3tio.a build/obj/*.o build/obj-ksp/*.o

"$CXX" -std=c++20 -W -Wall -Werror -Wfatal-errors -fsanitize=address,undefined -I src test/tio.cpp build/output/libcs3tio.a -o build/test/tio
for x in compiles invertible; do
for x in compiles roundtrip; do
  "$CXX" -std=c++20 -W -Wall -Werror -Wfatal-errors -fsanitize=address,undefined -I src test/$x.cpp build/output/libcs3tio.a -o build/test/$x
  build/test/$x
done

M ksp-src/cs3_tbl_entry.cpp => ksp-src/cs3_tbl_entry.cpp +6 -0
@@ 29,6 29,9 @@ namespace ksp {
            else if (on == std::string("MasterQuartzDummy")) {
                m_data = new master_quartz_dummy_t(m__io);
            }
            else if (on == std::string("QSChapter")) {
                m_data = new qschapter_t(m__io);
            }
            else if (on == std::string("OrbLineList")) {
                m_data = new orb_line_list_t(m__io);
            }


@@ 44,6 47,9 @@ namespace ksp {
            else if (on == std::string("SlotEp")) {
                m_data = new slot_ep_t(m__io);
            }
            else if (on == std::string("QSMons")) {
                m_data = new qsmons_t(m__io);
            }
            else if (on == std::string("SlotCost")) {
                m_data = new slot_cost_t(m__io);
            }

M ksp-src/cs3_tbl_entry.h => ksp-src/cs3_tbl_entry.h +4 -0
@@ 8,9 8,11 @@
#include "master_quartz_status.h"
#include "master_quartz_memo.h"
#include "slot_ep.h"
#include "qsmons.h"
#include "master_quartz_data.h"
#include "slot_cost.h"
#include "master_quartz_base.h"
#include "qschapter.h"
#include "base_list.h"
#include "master_quartz_dummy.h"
#include "quartz_cost.h"


@@ 23,9 25,11 @@ namespace ksp {
    class master_quartz_status_t;
    class master_quartz_memo_t;
    class slot_ep_t;
    class qsmons_t;
    class master_quartz_data_t;
    class slot_cost_t;
    class master_quartz_base_t;
    class qschapter_t;
    class base_list_t;
    class master_quartz_dummy_t;
    class quartz_cost_t;

A ksp-src/qschapter.cpp => ksp-src/qschapter.cpp +30 -0
@@ 0,0 1,30 @@
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "qschapter.h"
namespace ksp {

    qschapter_t::qschapter_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, qschapter_t* p__root) : kaitai::kstruct(p__io) {
        m__parent = p__parent;
        m__root = this;

        try {
            _read();
        } catch(...) {
            _clean_up();
            throw;
        }
    }

    void qschapter_t::_read() {
        m_section_id = m__io->read_s2le();
        m_section_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("UTF-8"));
        m_unknown = m__io->read_u2le();
    }

    qschapter_t::~qschapter_t() {
        _clean_up();
    }

    void qschapter_t::_clean_up() {
    }
}

A ksp-src/qschapter.h => ksp-src/qschapter.h +43 -0
@@ 0,0 1,43 @@
#ifndef QSCHAPTER_H_
#define QSCHAPTER_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
namespace ksp {

    class qschapter_t : public kaitai::kstruct {

    public:

        qschapter_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, qschapter_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~qschapter_t();

    private:
        int16_t m_section_id;
        std::string m_section_name;
        uint16_t m_unknown;
        qschapter_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        int16_t section_id() const { return m_section_id; }
        std::string section_name() const { return m_section_name; }
        uint16_t unknown() const { return m_unknown; }
        qschapter_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };
}

#endif  // QSCHAPTER_H_

A ksp-src/qsmons.cpp => ksp-src/qsmons.cpp +33 -0
@@ 0,0 1,33 @@
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "qsmons.h"
namespace ksp {

    qsmons_t::qsmons_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, qsmons_t* p__root) : kaitai::kstruct(p__io) {
        m__parent = p__parent;
        m__root = this;

        try {
            _read();
        } catch(...) {
            _clean_up();
            throw;
        }
    }

    void qsmons_t::_read() {
        m_enemy_script = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("UTF-8"));
        m_unknown_string = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("UTF-8"));
        m_section_id = m__io->read_s2le();
        m_unknown_short = m__io->read_u2le();
        m_is_extra = m__io->read_u1();
        m_unknown_byte = m__io->read_u1();
    }

    qsmons_t::~qsmons_t() {
        _clean_up();
    }

    void qsmons_t::_clean_up() {
    }
}

A ksp-src/qsmons.h => ksp-src/qsmons.h +62 -0
@@ 0,0 1,62 @@
#ifndef QSMONS_H_
#define QSMONS_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
namespace ksp {

    class qsmons_t : public kaitai::kstruct {

    public:

        qsmons_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, qsmons_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~qsmons_t();

    private:
        std::string m_enemy_script;
        std::string m_unknown_string;
        int16_t m_section_id;
        uint16_t m_unknown_short;
        uint8_t m_is_extra;
        uint8_t m_unknown_byte;
        qsmons_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        std::string enemy_script() const { return m_enemy_script; }
        std::string unknown_string() const { return m_unknown_string; }
        int16_t section_id() const { return m_section_id; }

        /**
         * In the original files, this takes values from 0 to 299 (inclusive) and is unique for every monster, suggesting it may be used as an ID or sort order.
         * However, swapping or duplicating values doesn't have any known effect.
         */
        uint16_t unknown_short() const { return m_unknown_short; }

        /**
         * 1 for every monster in the "EXTRA" section, 0 for all others.
         */
        uint8_t is_extra() const { return m_is_extra; }

        /**
         * In the original files, this byte is always 0.
         */
        uint8_t unknown_byte() const { return m_unknown_byte; }
        qsmons_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };
}

#endif  // QSMONS_H_

M src/cs3tio.h => src/cs3tio.h +17 -0
@@ 131,6 131,21 @@ struct QuartzCostEntry {
  SepithCost cost;
};

struct QSChapterEntry {
  int16_t section_id;
  std::string section_name;
  uint16_t unknown;
};

struct QSMonsEntry {
  std::string enemy_script;
  std::string unknown_string;
  int16_t section_id;
  uint16_t unknown_short;
  bool is_extra;
  uint8_t unknown_byte;
};

struct SlotCostEntry {
  uint16_t unlock_position; // position of the slot in its line
  uint16_t slot_level;


@@ 153,6 168,8 @@ typedef std::variant<
    OrbBaseEntry,
    OrbLineEntry,
    QuartzCostEntry,
    QSChapterEntry,
    QSMonsEntry,
    SlotCostEntry,
    SlotEPEntry,
    GenericEntry>

M src/header_name.cpp => src/header_name.cpp +6 -0
@@ 28,6 28,12 @@ struct HeaderNameVisitor {
  std::string_view operator()(const QuartzCostEntry &) {
    return std::string_view("QuartzCost");
  }
  std::string_view operator()(const QSChapterEntry &) {
    return std::string_view("QSChapter");
  }
  std::string_view operator()(const QSMonsEntry &) {
    return std::string_view("QSMons");
  }
  std::string_view operator()(const SlotCostEntry &) {
    return std::string_view("SlotCost");
  }

M src/load_tbl.cpp => src/load_tbl.cpp +28 -0
@@ 14,6 14,8 @@ static MasterQuartzStatusEntry convert(const ksp::master_quartz_status_t *);
static OrbBaseEntry convert(const ksp::base_list_t *);
static OrbLineEntry convert(const ksp::orb_line_list_t *);
static QuartzCostEntry convert(const ksp::quartz_cost_t *);
static QSChapterEntry convert(const ksp::qschapter_t *);
static QSMonsEntry convert(const ksp::qsmons_t *);
static SlotCostEntry convert(const ksp::slot_cost_t *);
static SlotEPEntry convert(const ksp::slot_ep_t *);



@@ 42,6 44,10 @@ Tbl load_tbl(std::istream &input) {
      entries.emplace_back(convert(dynamic_cast<const ksp::orb_line_list_t *>(entry->data())));
    } else if (entry->header_name() == "QuartzCost") {
      entries.emplace_back(convert(dynamic_cast<const ksp::quartz_cost_t *>(entry->data())));
    } else if (entry->header_name() == "QSChapter") {
      entries.emplace_back(convert(dynamic_cast<const ksp::qschapter_t *>(entry->data())));
    } else if (entry->header_name() == "QSMons") {
      entries.emplace_back(convert(dynamic_cast<const ksp::qsmons_t *>(entry->data())));
    } else if (entry->header_name() == "SlotCost") {
      entries.emplace_back(convert(dynamic_cast<const ksp::slot_cost_t *>(entry->data())));
    } else if (entry->header_name() == "SlotEp") {


@@ 201,6 207,28 @@ static QuartzCostEntry convert(const ksp::quartz_cost_t *entry) {
  };
}

static QSChapterEntry convert(const ksp::qschapter_t *entry) {
  return QSChapterEntry{
      .section_id = entry->section_id(),
      .section_name = entry->section_name(),
      .unknown = entry->unknown(),
  };
}

static QSMonsEntry convert(const ksp::qsmons_t *entry) {
  if (entry->is_extra() != 0 && entry->is_extra() != 1) {
    throw std::logic_error("is_extra byte should be 0 or 1");
  }
  return QSMonsEntry{
      .enemy_script = entry->enemy_script(),
      .unknown_string = entry->unknown_string(),
      .section_id = entry->section_id(),
      .unknown_short = entry->unknown_short(),
      .is_extra = (bool)entry->is_extra(),
      .unknown_byte = entry->unknown_byte(),
  };
}

static SlotCostEntry convert(const ksp::slot_cost_t *entry) {
  return SlotCostEntry{
      .unlock_position = entry->unlock_position(),

M src/store_tbl.cpp => src/store_tbl.cpp +21 -0
@@ 62,6 62,8 @@ struct StoreEntryVisitor {
  void operator()(const OrbBaseEntry &);
  void operator()(const OrbLineEntry &);
  void operator()(const QuartzCostEntry &);
  void operator()(const QSChapterEntry &);
  void operator()(const QSMonsEntry &);
  void operator()(const SlotCostEntry &);
  void operator()(const SlotEPEntry &);
  void operator()(const GenericEntry &);


@@ 211,6 213,25 @@ void StoreEntryVisitor::operator()(const QuartzCostEntry &entry) {
  write_u2_le(entry.cost.mirage, out);
}

void StoreEntryVisitor::operator()(const QSChapterEntry &entry) {
  write_strz(header_name(entry), out);
  write_u2_le(5 + entry.section_name.size(), out); // size
  write_s2_le(entry.section_id, out);
  write_strz(entry.section_name, out);
  write_u2_le(entry.unknown, out);
}

void StoreEntryVisitor::operator()(const QSMonsEntry &entry) {
  write_strz(header_name(entry), out);
  write_u2_le(8 + entry.enemy_script.size() + entry.unknown_string.size(), out); // size
  write_strz(entry.enemy_script, out);
  write_strz(entry.unknown_string, out);
  write_s2_le(entry.section_id, out);
  write_u2_le(entry.unknown_short, out);
  write_u1((uint8_t)entry.is_extra, out);
  write_u1(entry.unknown_byte, out);
}

void StoreEntryVisitor::operator()(const SlotCostEntry &entry) {
  write_strz(header_name(entry), out);
  write_u2_le(18, out); // size