~thestr4ng3r/mymcplus

ref: cff55ba378c47ff60022bf5bed1911a98d4b765b mymcplus/mymcplus/save/format_max_drive.py -rw-r--r-- 4.1 KiB
cff55ba3 — Kistelini Fix Usages of removed from/tostring for Python 3.9 (#10) 9 months ago
                                                                                
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
#
# This file is part of mymc+, based on mymc by Ross Ridge.
#
# mymc+ is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mymc+ is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mymc+.  If not, see <http://www.gnu.org/licenses/>.
#

import binascii

from .. import ps2mc_dir
from .. import utils
from . import ps2save
from . import lzari
from ..round import round_up
from .utils import *


FORMAT_ID = "max"

PS2SAVE_MAX_MAGIC = b"Ps2PowerSave"


def poll(hdr):
    return hdr.startswith(PS2SAVE_MAX_MAGIC)


def load2(save, f):
    (length, s) = save._compressed
    save._compressed = None

    if lzari is None:
        raise ps2mc_dir.Error("The lzari module is needed to decompress MAX Drive saves.")
    s = lzari.decode(s, length, "decompressing " + save.dirent[8].decode("ascii") + ": ")
    dirlen = save.dirent[2]
    timestamp = save.dirent[3]
    off = 0
    for i in range(dirlen):
        if len(s) - off < 36:
            raise ps2save.Eof(f)
        (l, name) = struct.unpack("<L32s", s[off: off + 36])
        name = utils.zero_terminate(name)
        # print "%08x %08x %s" % (off, l, name)
        off += 36
        data = s[off: off + l]
        if len(data) != l:
            raise ps2save.Eof(f)
        save.set_file(i,
                      (ps2mc_dir.DF_RWX | ps2mc_dir.DF_FILE | ps2mc_dir.DF_0400 | ps2mc_dir.DF_EXISTS,
                       0, l, timestamp, 0, 0, timestamp, 0,
                       name),
                      data)
        off += l
        off = round_up(off + 8, 16) - 8


def load(save, f, timestamp=None):
    s = f.read(0x5C)
    magic = None
    if len(s) == 0x5C:
        (magic, crc, dirname, iconsysname, clen, dirlen,
         length) = struct.unpack("<12sL32s32sLLL", s)
    if magic != PS2SAVE_MAX_MAGIC:
        raise ps2save.Corrupt("Not a MAX Drive save file", f)
    if clen == length:
        # some saves have the uncompressed size here
        # instead of the compressed size
        s = f.read()
    else:
        s = read_fixed(f, clen - 4)
    dirname = utils.zero_terminate(dirname)
    if timestamp == None:
        timestamp = ps2mc_dir.tod_now()
    save.set_directory((ps2mc_dir.DF_RWX | ps2mc_dir.DF_DIR | ps2mc_dir.DF_0400 | ps2mc_dir.DF_EXISTS,
                        0, dirlen, timestamp, 0, 0, timestamp, 0,
                        dirname),
                       f)
    save._compressed = (length, s)


def save(save, f):
    if lzari is None:
        raise ps2mc_dir.Error("The lzari module is needed to decompress MAX Drive saves.")

    iconsysname = ""
    icon_sys = save.get_icon_sys()
    if icon_sys != None:
        title = icon_sys.get_title("ascii")
        if len(title[0]) > 0 and title[0][-1] != ' ':
            iconsysname = title[0] + " " + title[1].strip()
        else:
            iconsysname = title[0] + title[1].rstrip()
    s = b""
    dirent = save.dirent
    for i in range(dirent[2]):
        (ent, data) = save.get_file(i)
        if not ps2mc_dir.mode_is_file(ent[0]):
            raise ps2mc_dir.Error("Non-file in save file.")
        s += struct.pack("<L32s", ent[2], ent[8])
        s += data
        s += b"\0" * (round_up(len(s) + 8, 16) - 8 - len(s))
    length = len(s)
    progress = "compressing " + dirent[8].decode("ascii") + ": "
    compressed = lzari.encode(s, progress)
    hdr = struct.pack("<12sL32s32sLLL", PS2SAVE_MAX_MAGIC,
                      0, dirent[8], iconsysname.encode("ascii"),
                      len(compressed) + 4, dirent[2], length)
    crc = binascii.crc32(hdr)
    crc = binascii.crc32(compressed, crc)
    f.write(struct.pack("<12sL32s32sLLL", PS2SAVE_MAX_MAGIC,
                        crc & 0xFFFFFFFF, dirent[8], iconsysname.encode("ascii"),
                        len(compressed) + 4, dirent[2], length))
    f.write(compressed)
    f.flush()