summaryrefslogtreecommitdiff
path: root/parse-pak/parse-pak.sh
blob: 9b5326e4ed57b4890ce0b5cc18665494c05b4b43 (plain)
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
#!/bin/bash
# Parse google-chrome .pak resourcefiles

parse_int() {
    ( echo "ibase=16; "; cat; echo -e "\n") | bc
}

endian_conversion() {
    if [[ "$1" == "little" ]]; then
        tac
    else
        cat
    fi
}

read_bytes() {
    local length=$1
    local endian=$2
    local i=0
    while [ $i -lt $length ]; do
        read -r -d ' ' byte
        [ "$byte" == "" ] && read -r -d ' ' byte
        echo $byte
        let i+=1
    done \
        | endian_conversion "$endian" \
        | tr -d '\n'
}

parse() {
    local format=$1
    local endian=big
    local i=0
    while [[ $i -lt ${#format} ]]; do
        case "${format:$i:1}" in
            "<") endian="little";;
            ">") endian="big";;
            "I") read_bytes "4" "$endian" | parse_int; let offset+=4;;
            "H") read_bytes "2" "$endian" | parse_int; let offset+=2;;
            "B") read_bytes "1" "$endian" | parse_int; let offset+=1;;
            *)
                echo "Invalid format: ${format:$i:1}" >&2;;
        esac
        let i+=1
    done | tr '\n' ' '
}

read_file() {
    local file=$1
    local offset=$2
    local format=$3
    dd if=$file bs=1 skip=$offset count=$(format_len "$format") 2>/dev/null \
        | od --output-duplicates --format=x1 --address-radix=n \
        | tr 'a-z' 'A-Z' | tr -d '\n'
}

parse_file() {
    read_file "$1" "$2" "$3" | parse "$3"
}

format_len() {
    local format=$1
    local len=0
    local i=0
    while [[ $i -lt ${#format} ]]; do
        case "${format:$i:1}" in
            "I") let len+=4 ;;
            "H") let len+=2 ;;
            "B") let len+=1 ;;
            "<"|">") ;;
            *) echo "Invalid format: ${format:$i:1}" >&2;;
        esac
        let i+=1
    done
    echo "$len"
}

##################
INPUT=$1
[ -e "$INPUT" ] || exit 1

header_size=$(format_len "<IIB")
entry_size=$(format_len "<HI")
read -r version num_entries encoding < \
    <(parse_file "$INPUT" 0 "<IIB")

if [ "$version" != "4" ]; then
    echo "Invalid version: $version" >&2
    exit 1
fi
if [ "$encoding" != "1" ]; then
    echo "Invalid encoding: $encoding" >&2
    exit 1
fi

pos=$header_size
i=0
while [ $i -lt $num_entries ]; do
    read -r id offset next_id next_offset < <(parse_file "$INPUT" $pos "<HIHI")
    let pos+=entry_size
    let entry_len=next_offset-offset
    echo "$id: $(dd if=$INPUT bs=1 count=$entry_len skip=$offset 2>/dev/null)"
    let i+=1
done