ddl_ar tool to work with ddls

This commit is contained in:
Ondřej Novák 2025-03-23 13:52:26 +01:00
parent 3eb73c4ad1
commit 054cc48e15
5 changed files with 183 additions and 0 deletions

3
tools/CMakeLists.txt Normal file
View file

@ -0,0 +1,3 @@
add_executable(ddl_ar ddl_ar.cpp ddl_ar_class.cpp)
set_property(TARGET ddl_ar PROPERTY CXX_STANDARD 20)

77
tools/ddl_ar.cpp Normal file
View file

@ -0,0 +1,77 @@
#include "ddl_ar.h"
#include "ddl_ar_class.h"
#include <iostream>
void show_licence_header() {
std::puts("Copyright (c) 2025 Ondrej Novak. All rights reserved.\n"
"This work is licensed under the terms of the MIT license.\n"
"For a copy, see <https://opensource.org/licenses/MIT>\n");
}
void show_short_help() {
show_licence_header();
std::puts("Use -h for help");
}
void show_help() {
show_licence_header();
std::puts("ddl_ar -h");
std::puts("ddl_ar <file.ddl> -l");
std::puts("ddl_ar <file.ddl> -x <files...>");
std::puts("");
std::puts("-h this help\n"
"file.ddl input ddl file\n"
"-l list of files\n"
"-x extract files\n");
}
int main(int argc, char **argv) {
if (argc <= 1) {
show_short_help();
return 1;
}
std::string_view ddl_file = argv[1];
if (ddl_file == "-h") {
show_help();
return 1;
}
if (argc <= 2) {
show_short_help();
return 1;
}
DDLArchive arch;
if (!arch.open(ddl_file)) {
std::cerr << "Failed to read ddl file: " << ddl_file;
return 1;
}
std::string_view swch(argv[2]);
if (swch == "-l") {
for (const auto &x: arch.get_directory()) {
std::puts(x.first.c_str());
}
} else if (swch == "-x") {
std::vector<std::string_view> fls;
fls.reserve(argc);
for (int i = 3; i < argc; ++i) fls.push_back(argv[i]);
arch.extract(fls.begin(), fls.end(), [](const DDLArchive::Extracted &x){
if (!x.found) {
std::cout << "Not found: " << x.name << "\n";
} else {
std::ofstream out(std::string(x.name), std::ios::out|std::ios::binary);
out.write(x.data.data(), x.data.size());
std::cout << "Extracted: " << x.name << " (" << x.data.size() << " bytes)\n";
}
});
} else {
std::cerr << "Unknown switch " << swch << std::endl;
}
return 0;
}

3
tools/ddl_ar.h Normal file
View file

@ -0,0 +1,3 @@
#pragma once

51
tools/ddl_ar_class.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "ddl_ar_class.h"
bool DDLArchive::open(std::filesystem::path ar_file) {
_directory.clear();
_ar_file = std::move(ar_file);
std::ifstream f = begin_read();
if (!f) return false;
f.seekg(4,std::ios::beg);
uint32_t dir_offset;
f.read(reinterpret_cast<char *>(&dir_offset), sizeof(dir_offset));
if (f.gcount() != sizeof(dir_offset)) return false;
uint32_t smallest_offset = std::numeric_limits<uint32_t>::max();
f.seekg(dir_offset, std::ios::beg);
while (f.tellg() < smallest_offset) {
char name[13];
f.read(name, 12);
if (f.gcount() != 12) return false;
name[12] = 0;
uint32_t offset;
f.read(reinterpret_cast<char *>(&offset), 4);
if (f.gcount() != 4) return false;
_directory.emplace(name, FileRec{offset, {}});
smallest_offset = std::min(offset, smallest_offset);
}
return true;
}
std::ifstream DDLArchive::begin_read() {
return std::ifstream(_ar_file, std::ios::in|std::ios::binary);
}
DDLArchive::Extracted DDLArchive::extract_file(std::ifstream &s,
std::string_view fname) {
auto iter = _directory.find(fname);
if (iter == _directory.end()) {
return {fname, false, {}};
}
s.seekg(iter->second.offset, std::ios::beg);
uint32_t sz;
s.read(reinterpret_cast<char *>(&sz),4);
if (s.gcount() != 4) return {fname, false, {}};
std::vector<char> data;
data.resize(sz);
s.read(data.data(), sz);
if (s.gcount() != sz) return {fname, false, {}};
return {fname, true, std::move(data)};
}

49
tools/ddl_ar_class.h Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include <string>
#include <filesystem>
#include <map>
#include <vector>
#include <fstream>
class DDLArchive {
public:
struct FileRec {
uint32_t offset;
std::filesystem::path source;
};
using Directory = std::map<std::string, FileRec, std::less<> >;
bool open(std::filesystem::path ar_file);
const Directory &get_directory() const {return _directory;}
const std::filesystem::path& get_ar_file() const {return _ar_file;}
struct Extracted {
std::string_view name;
bool found;
std::vector<char> data;
};
template<typename Iter, std::invocable<Extracted> CB>
void extract(Iter from, Iter to, CB &&cb) {
std::ifstream f = begin_read();
while (from != to) {
std::string_view n = *from;
cb(extract_file(f, n));
++from;
}
}
protected:
std::filesystem::path _ar_file;
Directory _directory;
std::ifstream begin_read();
Extracted extract_file(std::ifstream &s, std::string_view fname);
};