aboutsummaryrefslogtreecommitdiffstats
path: root/tools/rescompiler/main.cpp
blob: 31d07680c8cbf09797a842f922ebf3e6a079f35a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <library/cpp/resource/registry.h>

#include <util/stream/output.h>
#include <util/stream/file.h>
#include <util/digest/city.h>
#include <util/string/cast.h>
#include <util/string/hex.h>
#include <util/string/vector.h>
#include <util/string/split.h>

static inline void Formatter(IOutputStream& stream, std::string_view fmt, TVector<std::string_view>&& views) {
    constexpr auto INF = std::string::npos;
    size_t pos = 0;
    auto view = views.begin();
    while (pos != INF && pos < fmt.size()) {
        size_t found = fmt.find("{}", pos);
        stream << fmt.substr(pos, found == INF ? INF : found - pos);
        pos = found == INF ? INF : found + 2;
        if (view != views.end()) {
            stream << *view;
            ++view;
        }
    }
}

using namespace NResource;

static inline void EmitHex(IOutputStream& stream, const TString& view) {
    const unsigned char* data = reinterpret_cast<const unsigned char*>(view.data());
    size_t len = view.size();

    constexpr size_t CHAR2HEX = 5; // len('0x??,') == 5
    constexpr size_t MAX_STEP = 16;
    constexpr size_t STEPS[] = {MAX_STEP, 1};

    auto print = [](char* out, const unsigned char* iter, const unsigned char* end) {
        char templ[CHAR2HEX + 1] = "0x??,";
        while (iter != end) {
            HexEncode(iter, 1ULL, templ + 2ULL);
            memcpy(out, templ, CHAR2HEX);
            iter++;
            out += CHAR2HEX;
        }
    };

    char buf[CHAR2HEX * MAX_STEP + 4] = {};
    for (size_t step : STEPS) {
        while (len >= step) {
            print(buf, data, data + step);
            buf[CHAR2HEX * step] = 0;
            len -= step;
            data += step;
            stream << buf << (step > 1 ? "\n" : "");
        }
    }
    stream << '\n';
}

static inline void EmitHexArray(const TString& data, const TString& varName, IOutputStream& out) {
    const TString c = Compress(data);
    out << "static const unsigned char " << varName << "[] = {\n";
    EmitHex(out, c);
    out << "};\n";
}

static inline void GenOne(const TString& data, const TString& key, IOutputStream& out, bool isFile, bool useSections) {
    const TString name = "name" + ToString(CityHash64(key.data(), key.size()));

    if (useSections) {
        if (isFile) {
            Formatter(out, R"__(
            extern "C" const char {}[];
            extern "C" const char {}_end[];
            static const int REG_{} = NResource::LightRegisterI("{}", {}, {}_end);
            )__", {data, data, name, key, data, data});
        } else {
            EmitHexArray(data, name, out);
            Formatter(out, R"__(
            static const int REG_{} = NResource::LightRegisterS("{}", (const char*){}, sizeof({}));
            )__", {name, key, name, name});
        }
        return;
    }

    EmitHexArray(data, name, out);
    Formatter(out, R"__(
    static const NResource::TRegHelper REG_{}("{}", TStringBuf((const char*){}, sizeof({})));
    )__", {name, key, name, name});
}

static inline void EmitHeader(IOutputStream& out, bool useSections) {
    if (useSections) {
        out << R"__(
            // This function are defined in "library/cpp/resource/registry.cpp"
            namespace NResource {
                int LightRegisterS(const char*, const char*, unsigned long);
                int LightRegisterI(const char*, const char*, const char*);
            }
            )__";
    } else {
        out << "#include <library/cpp/resource/registry.h>\n";
    }
}

int main(int argc, char** argv) {
    if (argc < 4) {
        Cerr << "usage: " << argv[0] << " outfile [?--use-sections] [infile path]+ [- key=value]+" << Endl;
        return 1;
    }

    TFixedBufferFileOutput out(argv[1]);
    bool useSections = false;
    if (TStringBuf(argv[2]) == "--use-sections") {
        useSections = true;
        argv += 3;
    } else {
        argv += 2;
    }

    EmitHeader(out, useSections);

    while (*argv) {
        if ("-"sv == *argv) {
            TVector<TString> items = StringSplitter(TString(*(argv + 1))).Split('=').Limit(2).ToList<TString>();
            GenOne(TString(items[1]), TString(items[0]), out, false /*isFile*/, useSections);
        } else {
            const char* key = *(argv + 1);
            if (*key == '-') {
                ++key;
            }
            TString data = useSections ? *argv : TUnbufferedFileInput(*argv).ReadAll();
            GenOne(data, key, out, true /*isFile*/, useSections);
        }
        argv += 2;
    }
}