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
137
use crate::bres::*;
use crate::sakurai;
use crate::sakurai::ArcSakurai;
use crate::util;
use crate::wii_memory::WiiMemory;

use fancy_slice::FancySlice;

pub(crate) fn arc(data: FancySlice, wii_memory: &WiiMemory, item: bool) -> Arc {
    // read the main header
    let num_sub_headers = data.u16_be(6);
    let name = data.str(0x10).unwrap().to_string();

    // read the sub headers
    let mut children = vec![];
    let mut header_index = ARC_HEADER_SIZE;
    for i in 0..num_sub_headers {
        let mut arc_child = arc_child(data.relative_fancy_slice(header_index..));
        if arc_child.redirect_index == -1 {
            let tag = util::parse_tag(data.relative_slice(header_index + ARC_CHILD_HEADER_SIZE..));
            let child_data = data.relative_fancy_slice(header_index + ARC_CHILD_HEADER_SIZE..);
            arc_child.data = match tag.as_ref() {
                "ARC" => ArcChildData::Arc(arc(child_data, wii_memory, item)),
                "EFLS" => ArcChildData::Efls,
                "bres" => ArcChildData::Bres(bres(child_data)),
                "ATKD" => ArcChildData::Atkd,
                "REFF" => ArcChildData::Reff,
                "REFT" => ArcChildData::Reft,
                "AIPD" => ArcChildData::Aipd,
                "W" => ArcChildData::W,
                "" if i == 0 => ArcChildData::Sakurai(sakurai::arc_sakurai(
                    data.relative_fancy_slice(header_index + ARC_CHILD_HEADER_SIZE..),
                    wii_memory,
                    item,
                )),
                _ => ArcChildData::Unknown,
            };

            header_index += ARC_CHILD_HEADER_SIZE + arc_child.size as usize;

            // align to the next ARC_CHILD_HEADER_SIZE
            let offset = header_index % ARC_CHILD_HEADER_SIZE;
            if offset != 0 {
                header_index += ARC_CHILD_HEADER_SIZE - offset;
            }
            children.push(arc_child);
        }
    }

    Arc { name, children }
}

#[rustfmt::skip]
fn arc_child(data: FancySlice) -> ArcChild {
    ArcChild {
        ty:             data.i16_be(0),
        index:          data.i16_be(2),
        size:           data.i32_be(4),
        group_index:    data.u8(8),
        redirect_index: data.i16_be(10),
        data:           ArcChildData::Unknown,
    }
}

impl Arc {
    pub fn compile(&self) -> Vec<u8> {
        // TODO: Would be more efficient to allocate once, then overwrite the bytes at specific offsets.
        // However, for now, having each section create its own vec which get `extend`ed together makes for a cleaner implementation.
        let mut output = Vec::with_capacity(1024 * 1024); // Preallocate 1MB, we will likely need more, but dont want to overdo it as we have arcs in arcs.

        // create arc header
        output.extend("ARC".chars().map(|x| x as u8));
        output.extend(&[0x00, 0x01, 0x01]); // TODO: ??
        output.extend(&u16::to_be_bytes(self.children.len() as u16));
        output.extend(&[0x00; 8]);
        output.extend(self.name.chars().map(|x| x as u8));
        while output.len() < ARC_HEADER_SIZE {
            output.push(0x00);
        }

        for child in &self.children {
            // create arc child header
            let start = output.len();
            output.extend(&i16::to_be_bytes(child.ty));
            output.extend(&i16::to_be_bytes(child.index));
            output.extend(&i32::to_be_bytes(child.size)); // TODO: remove this field and calculate it instead
            output.push(child.group_index);
            output.push(0x00);
            output.extend(&i16::to_be_bytes(child.redirect_index));
            while output.len() < start + ARC_CHILD_HEADER_SIZE {
                output.push(0x00);
            }

            match &child.data {
                ArcChildData::Arc(arc) => output.extend(arc.compile()),
                ArcChildData::Bres(bres) => output.extend(bres.compile()),
                // TODO
                _ => {}
            }
        }

        output
    }
}

const ARC_HEADER_SIZE: usize = 0x40;
/// Arc is for archive not to be confused with an atomic reference count
#[derive(Clone, Debug)]
pub struct Arc {
    pub name: String,
    pub children: Vec<ArcChild>,
}

const ARC_CHILD_HEADER_SIZE: usize = 0x20;
#[derive(Clone, Debug)]
pub struct ArcChild {
    ty: i16,
    index: i16,
    size: i32,
    group_index: u8,
    redirect_index: i16, // The index of a different file to read
    pub data: ArcChildData,
}

#[derive(Clone, Debug)]
pub enum ArcChildData {
    Arc(Arc),
    Sakurai(ArcSakurai),
    Efls,
    Bres(Bres),
    Atkd,
    Reff,
    Reft,
    Aipd,
    W,
    Unknown,
}