Skip to main content

fable_format/stb/
mod.rs

1mod decode;
2mod encode;
3
4use std::io::BufReader;
5
6#[derive(Debug,PartialEq)]
7pub struct Stb {
8    pub header: StbHeader,
9    pub entries_header: StbEntriesHeader,
10    pub entries: Vec<StbEntry>,
11}
12
13#[derive(Debug,PartialEq)]
14pub struct StbHeader {
15    pub version: u32,
16    pub header_size: u32,
17    pub files_count: u32,
18    pub levels_count: u32,
19    pub entries_offset: u32,
20}
21
22#[derive(Debug,PartialEq)]
23pub struct StbEntriesHeader {
24    pub start: u32,
25    pub levels_count: u32,
26}
27
28#[derive(Debug,PartialEq)]
29pub struct StbEntry {
30    pub listing_start: u32,
31    pub id: u32,
32    pub offset: u32,
33    pub length: u32,
34    pub name_1: String,
35    pub name_2: String,
36    pub extras: Option<StbEntryExtras>,
37}
38
39#[derive(Debug,PartialEq)]
40pub struct StbEntryExtras {
41    pub field_1: u32,
42    pub field_2: u32,
43    pub field_3: u32,
44    pub field_4: u32,
45}
46
47#[derive(Debug)]
48pub struct StbReader<Source> {
49    pub source: BufReader<Source>,
50    pub entry: StbEntry,
51}
52
53/// Temporary comments from fabletlcmod.com.
54///
55/// ```txt
56/// Stb .Lev
57///
58/// For Easier Editing Open STATIC_MAP_COMMON_HEADER Last Entry in .Stb Archive
59///
60/// Static Map Common Header
61///
62/// [4] Bytes - Number of Entries
63///
64/// Then You Need to Search for Filename (Data\Levels\FinalAlbion\xxxxx.Lev)
65/// Once Desired Name is found there is an offset jump following name.
66///
67/// [4] Bytes - Offset Jump to File Data
68///
69/// [4] Bytes - Start Data (01 00 00 00)
70/// [4] Bytes - Level ID
71/// [4] Bytes - Unknown (Relative to Dimension of Map)
72/// [4] Bytes - Map Dimension (X)
73/// [4] Bytes - Map Dimesnion (Y)
74/// [4] Bytes - Map World Offset (X)
75/// [4] Bytes - Map World Offset (Y)
76/// [4] Bytes - Null
77/// [4] Bytes - Current File Offset Push
78/// [4] Bytes - Current File Offset Push
79///
80/// [4] Bytes - New Section Start (0A 00 00 00)
81/// [4] Bytes - Always (04 00 00 00)
82/// [4] Bytes - Repeat of Unknown Data Above (Relative to Dimension of Map)
83/// [4] Bytes - Checksum (Same as one used in .Wad Lev file)
84/// [4] Bytes - Unknown (Always B3 16 34 7C?)
85/// [4] Bytes - Unknown (Always 00 00 32 00)
86/// [4] Bytes - Map World Offset Starting Position (X) {Float}
87/// [4] Bytes - Map World Offset Starting Position (Y) {Float}
88/// [4] Bytes - Map World Offset Starting Position (Z) {Float}
89/// [4] Bytes - Map World Offset Ending Position (X) {Float}
90/// [4] Bytes - Map World Offset Ending Position (Y) {Float}
91/// [4] Bytes - Map World Offset Ending Position (Z) {Float}
92/// [4] Bytes - Current File Offset Push
93/// [4] Bytes - Current File Offset Push
94///
95/// [4] Bytes - New Section Start (01 00 00 00) (.Lev File Offsets)
96/// [4] Bytes - Offset to First Table In Desired .Lev
97/// [4] Bytes - Offset to Second Table In Desired .Lev
98/// [4] Bytes - Second Table Size
99/// [1] Byte - Indicator For Offset Push
100/// [4] Bytes - Current File Offset Push
101///
102///
103/// [4] Bytes - Section Length In Bytes *(Does not Start until after Next 4 Bytes)
104/// [4] Bytes - New Section Start (01 E3 E3 12) (Flora Placement?)
105/// [4] Bytes - World Placement (X) {Float}
106/// [4] Bytes - World Placement (Y) {Float}
107/// [4] Bytes - Height Placement (Z) {Float}
108/// [4] Bytes - Unknown Placement (Rotation?) {Float}
109/// [4] Bytes - Unknown Placement {Float}
110/// [4] Bytes - Sections for Flora Data (In Static Map Header) (If Present)
111/// [4] Bytes - Offset to Third Section (If Present) in Current .Lev
112/// [4] Bytes - Size of Third Section (If Present) in Current .Lev
113/// [4] Bytes - Offset in Third Section table (Third Section Offset + these bytes)
114/// [4] Bytes - Null
115/// [2] Bytes - Map Dimension (X)
116/// [2] Bytes - Map Dimension (Y)
117///
118/// [1] Byte - Indicator for Internal Loop Within Last Segment
119/// [4] Bytes - Number of Sections to loop *
120///
121/// *If Zero Data Ends Here for Current Map. If there is Value follow the next segment
122/// Flora Loop
123///
124/// [4] Bytes - BIG ID #1
125/// [4] Bytes - BIG ID #2
126/// [4] Bytes - BIG ID #3
127/// [4] Bytes - Placement (X)? {Float}
128/// [4] Bytes - Placement (Y)? {Float}
129/// [4] Bytes - Unknown (Area?) (Int)
130/// [4] Bytes - Null (Int)
131/// [4] Bytes - Unknown (Rotation/Angle?) {Float}
132/// [4] Bytes - Unknown (Rotation/Angle?) {Float}
133/// [4] Bytes - Unknown (Rotation/Angle?) {Float}
134/// [4] Bytes - Placement on Map (X) (Secondary?) {Float}
135/// [4] Bytes - Placement on Map (Y) (Secondary?) {Float}
136/// [4] Bytes - Number of Sections (Int)
137/// [4] Bytes - Unknown (type?) (Int)
138/// [1] Byte - Boolean? Unknown
139/// [1] Byte - Boolean? Unknown
140/// [1] Byte - Boolean? Unknown
141/// [1] Byte - Boolean? Unknown
142/// [4] Bytes - Unknown (Int)
143/// .Lev File Spec
144/// First Table (Height)
145///
146/// Each Row is 36 Bytes long. Number of Rows is relative to ((X*Y) / 256) (dimensions of map divided by area of section /// always 16×16)
147///
148/// [4] Bytes - Offset
149/// [4] Bytes - Compressed Size
150/// [4] Bytes - World Map Starting Position (X) {Float}
151/// [4] Bytes - World Map Starting Position (Y) {Float}
152/// [4] Bytes - World Map Starting Position (Z) {Float}
153/// [4] Bytes - World Map Ending Position (X) {Float}
154/// [4] Bytes - World Map Ending Position (Y) {Float}
155/// [4] Bytes - World Map Ending Position (Z) {Float}
156/// [4] Bytes - Unknown
157///
158/// *Each Compressed Package is LZO Compressed following this format:
159/// [4] Bytes - Decompressed Size
160/// [4] Bytes - Compressed Size
161/// Decompressed Data
162///
163/// [2] Bytes - Number of Sections
164///
165/// Looped
166/// [2] Bytes - Number of Verts
167/// [2] Bytes - Number of Faces
168/// [1] Byte - Null
169/// [4] Bytes - Textures.Big ID of Image used. (This may be only for Proc Textures) These are the actual Mesh
170/// [4] Bytes - Textures.Big ID of Image used. (This may be repeated from above)
171/// [4] Bytes - Textures.Big ID of Bump used.
172/// [4] Bytes - Possibly another texture link
173/// [4] Bytes - Possibly another texture link
174/// [4] Bytes - Possibly another texture link
175/// [1] Byte - Null
176/// Vert Listing
177///
178/// 15 Byte rows
179///
180/// [2] Bytes - Global X Position
181/// [2] Bytes - Global Y Position
182/// [4] Bytes - Height {Float}
183/// [4] Bytes - Packed Normal {PackedXYZ}
184/// [1] Byte - 00 or FF (indicates next X or Y value?)
185/// [1] Bytes - TU offset, Actual TU = (TU + (this-127)/127)
186/// [1] Bytes - TU offset, Actual TU = (TU + (this-127)/127)
187/// Faces
188///
189/// 2 Bytes
190/// [2] Bytes - Number of Faces
191/// Second Table (Unused Graphics?)
192///
193/// Originally believed to be the mesh, could be used for internal editor only.
194/// [2] Bytes - Placement (X)
195/// [2] Bytes - Placement (Y)
196/// [2] Bytes - Tile Value (X) (Based on Dimension)*
197/// [2] Bytes - Tile Value (Y) (Based on Dimension)*
198///
199/// *If Value is size of Map then this is the lowest LOD
200///
201/// [1] Byte - Indicator (01 - Indicates that it contains Offsets. Else it is only 47 Bytes in Length)
202/// [1] Byte - Indicator (Section Start)
203/// [1] Byte - Indicator (Section End)
204/// [4] Bytes - Offset to Compressed Chunk
205/// [4] Bytes - Size of Entire Table (If Value is present this indicates a start of a new LOD)
206/// [4] Bytes - Offset Push Within Table (Next Section to Read, Table treated as internal file!)
207/// [4] Bytes - World Map Start Position (X) {Float}
208/// [4] Bytes - World Map Start Position (Y) {Float}
209/// [4] Bytes - World Map Start Position (Z) {Float}
210/// [4] Bytes - World Map Ending Position (X) {Float}
211/// [4] Bytes - World Map Ending Position (Y) {Float}
212/// [4] Bytes - World Map Ending Position (Z) {Float}
213/// Offset Rows
214///
215/// If Indicator = 01 above then loop until starting indicator equals (stop indicator above)
216/// 13 Byte Rows
217///
218/// [1] Byte - Indicator (Row start based on LOD)
219/// [4] Bytes - Offset to compressed chunk
220/// [4] Bytes - Compressed Chunk Size
221/// [4] Bytes - Offset to specific compressed Data (Offset to compressed chunk + Value of These Bytes)
222///
223/// *Additional Notes Number of LODs seems dependent on First Table Rows. Example Creature Hub 4 Rows in first Table. // Graphics It Contains (4 High Quality, 6 Medium, 1 Low)
224///
225/// *Each Compressed Package is LZO Compressed following this format:
226/// [4] Bytes - Decompressed Size
227/// [4] Bytes - Compressed Size
228/// Decompressed Data
229///
230/// [2] Bytes - Image Dimension (X) it takes up on Map *
231/// [2] Bytes - Image Dimension (Y it takes up on Map *
232///
233/// *If Dimension equals map dimensions then it is the lowest LOD
234///
235/// [2] Bytes - Tile Placement of Image (Based on Map Dimensions) (X)
236/// [2] Bytes - Tile Placement of Image (Based on Map Dimensions) (Y)
237/// [1] Byte - Null
238/// [1] Byte - Indicator for LOD (not an actual value!)
239/// [3] Bytes - Unkown Based on LOD
240/// [1] Byte - Indicator new section start
241/// [1] Byte - Image Dimensions (X) (Actual Image Size, for editing)
242/// [1] Byte - Image Dimensions (Y) (Actual Image Size, for editing)
243/// [1] Byte - Indicator new section start
244/// [2] Bytes - Image Dimensions Repeat (X) as 2 bytes
245/// [2] Bytes - Image Dimensions Repeat (Y) as 2 bytes
246/// [1] Byte - Indicator new section start
247/// [4] Bytes - Unknown
248/// [6] Bytes - Null?
249/// [4] Bytes - Always (01 00 00 00) Indicates Image Start
250/// ~ DXT1 Image (To get Image size take actual dimension ( X * Y / 2)
251///
252/// *The Rest of file is currently Unknown
253/// Third Section (Flora/Model)
254///
255/// The only offset to this section exists in Static Map Header see: [4] Bytes - Offset in Third Section table (Third // Section Offset + these bytes)
256///
257/// *The following entries are very sloppy and jump around quite a bit.
258///
259/// [4] Bytes - always zero?
260/// [4] Bytes - if zero again it will need to read until it is at least reaches 1.
261/// [4] Bytes - Global X coords [Float]
262/// [4] Bytes - Global Y coords [Float]
263/// [4] Bytes - Global Z coords [Float]
264/// [4] Bytes - Unknown (Rotation?) [Float]
265/// [4] Bytes - Unknown (Always 23?) [Float]
266/// [4] Bytes - Unknown [Integer]
267/// [4] Bytes - Offset to Chunk [Integer]
268/// [4] Bytes - Chunk Size [Integer]
269/// [4] Bytes - Offset in Chunk (take above Offset + This) [Integer]
270/// [2] Bytes - Placement X? [Short]
271/// [2] Bytes - Placement Y? [Short]
272/// [2] Bytes - Size X? [Short]
273/// [2] Bytes - Size Y? [Short]
274/// [1] Byte - Indicates more entries. If zero reading is finished.
275///
276/// Here is the Tricky Part. If Offset above leads directly to anything other than zero you read below, else the entry is // dead and the loop above is redone.
277///
278/// [4] Bytes - Number of Sections (Array) [Integer]
279/// [4] Bytes - Chunk Offset [Integer]
280/// [4] Bytes - Chunk Size [Integer]
281/// [4] Bytes - Package Offset (Chunk Offset + Package Offset) (This will lead to compressed data) [Integer]
282/// [4] Bytes - Global X coords [Float]
283/// [4] Bytes - Global Y coords [Float]
284/// [4] Bytes - Global Z coords [Float]
285/// [4] Bytes - Unknown (Rotation?) [Float]
286/// [4] Bytes - Unknown [Float]
287/// [4] Bytes - Unknown [Integer]
288/// [4] Bytes - Unknown [Integer]
289/// Compressed Package
290///
291/// [4] Bytes - Decompressed Size
292/// [4] Bytes - Compressed Size
293/// *Data is LZO compressed
294/// Decompressed Data
295///
296///
297///
298///
299/// 1bit sign, 4bits exponent, 27bits mantissa
300///
301/// value = (-1^s)*(2^(e-7))*(f/0x8000000)
302///
303/// s eeee fff ffffffff ffffffff ffffffff  value
304/// 0 0111 100 00100110 11010110 11010100  0.5189644396305084228515625
305/// 0 0111 011 10011100 11000011 11110010  0.45154561102390289306640625
306/// 0 0111 000 10100000 11010110 01001001  0.078533716499805450439453125
307/// 1 0111 100 00100110 11000100 10101001 -0.518929786980152130126953125
308/// 0 0111 011 10011100 10011011 00110000  0.45146787166595458984375
309/// 0 0111 001 11000100 10001101 10010110  0.22097317874431610107421875
310/// 0 0111 001 10100000 11001111 10101001  0.203521080315113067626953125
311/// 1 0111 001 01101111 11100110 11100001 -0.179639585316181182861328125
312/// 0 0111 100 00111000 00111010 10001101  0.527455426752567291259765625
313///
314/// 0 0111 011 01110001 00111100 11110011  0.430292032659053802490234375
315/// 1 0111 100 00000001 10000100 01011111 -0.500740759074687957763671875
316/// 0 0111 010 10111000 00110110 10110101  0.339948095381259918212890625
317/// 0 0111 100 00000010 00011000 01011010  0.50102300941944122314453125
318/// 0 0111 011 01111001 11101000 10000110  0.43452553451061248779296875
319/// 0 0111 010 00101010 11011000 00111011  0.270920239388942718505859375
320/// 1 0111 010 10011011 11111011 10001000 -0.326163351535797119140625
321/// 0 0111 010 10000010 00000001 01011100  0.3134791553020477294921875
322/// 0 0111 100 00001110 01111000 10011110  0.50706599652767181396484375
323///
324///
325///
326/// Also the game mostly uses lzo1x compression, and dxt(textures).
327/// ```
328pub struct StbLev {
329}