1
+ #include " wad.hpp"
2
+
3
+ using namespace rlib ;
4
+ using namespace rlib ::ar;
5
+
6
+ #define ar_assert (...) \
7
+ do { \
8
+ if (!(__VA_ARGS__)) return " WAD::read: " #__VA_ARGS__; \
9
+ } while (false )
10
+
11
+ struct WAD ::Header {
12
+ struct Base ;
13
+ struct V1 ;
14
+ struct V2 ;
15
+ struct V3 ;
16
+
17
+ std::size_t entry_size;
18
+ std::size_t entry_count;
19
+ std::size_t toc_start;
20
+ std::size_t toc_size;
21
+ };
22
+
23
+ struct WAD ::Header::Base {
24
+ std::array<char , 2 > magic;
25
+ std::uint8_t version[2 ];
26
+ };
27
+
28
+ struct WAD ::Header::V1 : Base {
29
+ std::uint16_t toc_start;
30
+ std::uint16_t entry_size;
31
+ std::uint32_t entry_count;
32
+ };
33
+
34
+ struct WAD ::Header::V2 : Base {
35
+ std::array<std::uint8_t , 84 > signature;
36
+ std::array<std::uint8_t , 8 > checksum;
37
+ std::uint16_t toc_start;
38
+ std::uint16_t entry_size;
39
+ std::uint32_t entry_count;
40
+ };
41
+
42
+ struct WAD ::Header::V3 : Base {
43
+ std::uint8_t signature[256 ];
44
+ std::array<std::uint8_t , 8 > checksum;
45
+ static constexpr std::uint16_t toc_start = 272 ;
46
+ static constexpr std::uint16_t entry_size = 32 ;
47
+ std::uint32_t entry_count;
48
+ };
49
+
50
+ struct WAD ::Entry::Raw {
51
+ std::uint64_t path;
52
+ std::uint32_t offset;
53
+ std::uint32_t size_compressed;
54
+ std::uint32_t size_uncompressed;
55
+ std::uint8_t type : 4 ;
56
+ std::uint8_t subchunks : 4 ;
57
+ std::uint8_t pad[3 ];
58
+ };
59
+
60
+ auto WAD::check_magic (std::span<char const > data) noexcept -> bool {
61
+ return data.size () >= 4 && std::memcmp (data.data (), " RW" , 2 ) == 0 && (uint8_t )data[2 ] <= 10 ;
62
+ }
63
+
64
+ auto WAD::read (IO const & io, std::size_t offset, std::size_t size) -> char const * {
65
+ static constexpr auto MAGIC = std::array{' R' , ' W' };
66
+
67
+ Header::Base header_base = {};
68
+ ar_assert (size >= sizeof (header_base));
69
+ io.read (offset, {(char *)&header_base, sizeof (header_base)});
70
+ ar_assert (header_base.magic == MAGIC);
71
+
72
+ Header header = {};
73
+ switch (header_base.version [0 ]) {
74
+ #define read_header ($V) \
75
+ do { \
76
+ Header::V##$V v_header = {}; \
77
+ ar_assert (size >= sizeof (header)); \
78
+ io.read (offset, {(char *)&v_header, sizeof (v_header)}); \
79
+ header.entry_size = v_header.entry_size ; \
80
+ header.entry_count = v_header.entry_count ; \
81
+ header.toc_start = v_header.toc_start ; \
82
+ header.toc_size = header.entry_size * header.entry_count ; \
83
+ } while (false )
84
+ case 0 :
85
+ case 1 :
86
+ read_header (1 );
87
+ break ;
88
+ case 2 :
89
+ read_header (2 );
90
+ break ;
91
+ case 3 :
92
+ read_header (3 );
93
+ break ;
94
+ #undef read_header
95
+ default :
96
+ return " Unknown wad version" ;
97
+ }
98
+ ar_assert (size >= header.toc_start );
99
+ ar_assert (size - header.toc_start >= header.toc_size );
100
+ header.toc_start += offset;
101
+
102
+ entries.clear ();
103
+ entries.reserve (header.entry_count + 1 );
104
+
105
+ entries.push_back (Entry{
106
+ .offset = header.toc_start ,
107
+ .size = header.toc_size ,
108
+ .compressed = false ,
109
+ });
110
+ for (std::size_t i = 0 ; i != header.entry_count ; ++i) {
111
+ auto raw_entry = Entry::Raw{};
112
+ io.read (header.toc_start + i * header.entry_size , {(char *)&raw_entry, header.entry_size });
113
+
114
+ auto entry = Entry{
115
+ .offset = offset + raw_entry.offset ,
116
+ .size = raw_entry.size_compressed ,
117
+ .compressed = raw_entry.type != 0 ,
118
+ };
119
+ ar_assert (entry.offset >= header.toc_start + header.toc_size );
120
+ ar_assert (size >= entry.offset );
121
+ ar_assert (size - entry.offset >= entry.size );
122
+ entries.push_back (entry);
123
+ }
124
+
125
+ return nullptr ;
126
+ }
0 commit comments