diff --git a/lib/bytes.cc b/lib/bytes.cc index c09749b0..0807a9cc 100644 --- a/lib/bytes.cc +++ b/lib/bytes.cc @@ -2,6 +2,7 @@ #include "bytes.h" #include "fmt/format.h" #include "common/crunch.h" +#include #include static std::shared_ptr> createVector(unsigned size) @@ -280,6 +281,16 @@ Bytes Bytes::uncrunch() const return output; } +void Bytes::writeToFile(const std::string& filename) const +{ + std::ofstream f(filename, std::ios::out | std::ios::binary); + if (!f.is_open()) + Error() << fmt::format("cannot open output file '{}'", filename); + + f.write((const char*) cbegin(), size()); + f.close(); +} + ByteReader Bytes::reader() const { return ByteReader(*this); diff --git a/lib/bytes.h b/lib/bytes.h index 458130ed..3656fadb 100644 --- a/lib/bytes.h +++ b/lib/bytes.h @@ -56,6 +56,8 @@ public: ByteReader reader() const; ByteWriter writer(); + void writeToFile(const std::string& filename) const; + private: std::shared_ptr> _data; unsigned _low; diff --git a/lib/ldbs.cc b/lib/ldbs.cc new file mode 100644 index 00000000..9efaae10 --- /dev/null +++ b/lib/ldbs.cc @@ -0,0 +1,81 @@ +#include "globals.h" +#include +#include "bytes.h" +#include "ldbs.h" +#include "fmt/format.h" + +LDBS::LDBS() +{} + +uint32_t LDBS::put(const Bytes& data, uint32_t type) +{ + uint32_t address = top; + Block& block = blocks[address]; + block.type = type; + block.data = data; + + top += data.size() + 20; + return address; +} + +uint32_t LDBS::read(const Bytes& data) +{ + ByteReader br(data); + + br.seek(0); + if ((br.read_be32() != LDBS_FILE_MAGIC) + || (br.read_be32() != LDBS_FILE_TYPE)) + Error() << "not a valid LDBS file"; + + uint32_t address = br.read_le32(); + br.skip(4); + uint32_t trackDirectory = br.read_le32(); + + while (address) + { + br.seek(address); + if (br.read_be32() != LDBS_BLOCK_MAGIC) + Error() << fmt::format("invalid block at address 0x{:x}", address); + + Block& block = blocks[address]; + block.type = br.read_be32(); + + uint32_t size = br.read_le32(); + br.skip(4); + address = br.read_le32(); + + block.data.writer().append(br.read(size)); + } + + top = data.size(); + return trackDirectory; +} + +const Bytes LDBS::write(uint32_t trackDirectory) +{ + Bytes data(top); + ByteWriter bw(data); + + uint32_t previous = 0; + for (const auto& e : blocks) + { + bw.seek(e.first); + bw.write_be32(LDBS_BLOCK_MAGIC); + bw.write_be32(e.second.type); + bw.write_le32(e.second.data.size()); + bw.write_le32(e.second.data.size()); + bw.write_le32(previous); + bw.append(e.second.data); + + previous = e.first; + } + + bw.seek(0); + bw.write_be32(LDBS_FILE_MAGIC); + bw.write_be32(LDBS_FILE_TYPE); + bw.write_le32(previous); + bw.write_le32(0); + bw.write_le32(trackDirectory); + + return data; +} diff --git a/lib/ldbs.h b/lib/ldbs.h new file mode 100644 index 00000000..1842e1dc --- /dev/null +++ b/lib/ldbs.h @@ -0,0 +1,41 @@ +#ifndef LDBS_H +#define LDBS_H + +class Bytes; + +/* A very simple interface to John Elliott's LDBS image format: + * http://www.seasip.info/Unix/LibDsk/ldbs.html + */ + +#define LDBS_FILE_MAGIC 0x4C425301 /* "LBS\01" */ +#define LDBS_FILE_TYPE 0x44534B02 /* "DSK\02" */ +#define LDBS_BLOCK_MAGIC 0x4C444201 /* "LDB\01" */ +#define LDBS_TRACK_BLOCK 0x44495201 /* "DIR\01" */ + +class LDBS +{ +public: + LDBS(); + +public: + const Bytes& get(uint32_t address) const + { return blocks.at(address).data; } + + uint32_t put(const Bytes& data, uint32_t type); + +public: + const Bytes write(uint32_t trackDirectory); + uint32_t read(const Bytes& bytes); + +private: + struct Block + { + uint32_t type; + Bytes data; + }; + + std::map blocks; + unsigned top = 20; +}; + +#endif diff --git a/mkninja.sh b/mkninja.sh index dbc3f010..05afd640 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -137,6 +137,10 @@ buildlibrary libfmt.a \ dep/fmt/posix.cc \ buildlibrary libbackend.a \ + lib/imagereader/imagereader.cc \ + lib/imagereader/imgimagereader.cc \ + lib/imagewriter/imagewriter.cc \ + lib/imagewriter/imgimagewriter.cc \ arch/aeslanier/decoder.cc \ arch/amiga/decoder.cc \ arch/apple2/decoder.cc \ @@ -168,13 +172,10 @@ buildlibrary libbackend.a \ lib/fluxsource/kryoflux.cc \ lib/fluxsource/sqlitefluxsource.cc \ lib/fluxsource/streamfluxsource.cc \ - lib/imagereader/imagereader.cc \ - lib/imagereader/imgimagereader.cc \ - lib/imagewriter/imagewriter.cc \ - lib/imagewriter/imgimagewriter.cc \ lib/globals.cc \ lib/hexdump.cc \ lib/image.cc \ + lib/ldbs.cc \ lib/reader.cc \ lib/sector.cc \ lib/sectorset.cc \ @@ -227,12 +228,13 @@ buildsimpleprogram brother120tool \ libemu.a \ libfmt.a \ +runtest bitaccumulator-test tests/bitaccumulator.cc +runtest bytes-test tests/bytes.cc +runtest compression-test tests/compression.cc +runtest crunch-test tests/crunch.cc runtest dataspec-test tests/dataspec.cc runtest flags-test tests/flags.cc -runtest fmmfm-test tests/fmmfm.cc -runtest bitaccumulator-test tests/bitaccumulator.cc -runtest kryoflux-test tests/kryoflux.cc -runtest compression-test tests/compression.cc -runtest bytes-test tests/bytes.cc -runtest crunch-test tests/crunch.cc runtest fluxpattern-test tests/fluxpattern.cc +runtest fmmfm-test tests/fmmfm.cc +runtest kryoflux-test tests/kryoflux.cc +runtest ldbs-test tests/ldbs.cc diff --git a/tests/ldbs.cc b/tests/ldbs.cc new file mode 100644 index 00000000..3e017bf6 --- /dev/null +++ b/tests/ldbs.cc @@ -0,0 +1,70 @@ +#include +#include +#include +#include "globals.h" +#include "bytes.h" +#include "ldbs.h" + +static Bytes testdata +{ + 'L', 'B', 'S', 0x01, 'D', 'S', 'K', 0x02, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x12, 0x00, 0x00, 'L', 'D', 'B', 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 'L', 'D', 'B', 0x01, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x02 +}; + +static void assertBytes(const Bytes& want, const Bytes& got) +{ + if (want != got) + { + std::cout << "Wanted bytes:" << std::endl; + hexdump(std::cout, want); + std::cout << std::endl << "Produced bytes:" << std::endl; + hexdump(std::cout, got); + abort(); + } +} +static void test_getset() +{ + LDBS ldbs; + + uint32_t block1 = ldbs.put(Bytes { 1 }, 1); + uint32_t block2 = ldbs.put(Bytes { 2 }, 2); + assert(block1 != block2); + + assert(ldbs.get(block1) == Bytes { 1 }); + assert(ldbs.get(block2) == Bytes { 2 }); +} + +static void test_write() +{ + LDBS ldbs; + + uint32_t block1 = ldbs.put(Bytes { 1 }, 1); + uint32_t block2 = ldbs.put(Bytes { 2 }, 2); + Bytes data = ldbs.write(0x1234); + + assertBytes(testdata, data); +} + +static void test_read() +{ + LDBS ldbs; + uint32_t trackDirectory = ldbs.read(testdata); + + assert(trackDirectory == 0x1234); + assert(ldbs.get(0x14) == Bytes { 1 }); + assert(ldbs.get(0x29) == Bytes { 2 }); +} + +int main(int argc, const char* argv[]) +{ + test_getset(); + test_write(); + test_read(); + return 0; +}