mirror of
https://github.com/ps2dev/mymc.git
synced 2024-11-24 03:20:10 +01:00
813 lines
20 KiB
Python
Executable File
813 lines
20 KiB
Python
Executable File
#
|
|
# mymc.py
|
|
#
|
|
# By Ross Ridge
|
|
# Public Domain
|
|
#
|
|
|
|
"""A utility for manipulating PS2 memory card images."""
|
|
|
|
_SCCS_ID = "@(#) mysc mymc.py 1.12 12/10/04 19:09:16\n"[:-1]
|
|
|
|
import sys
|
|
import os
|
|
import time
|
|
import optparse
|
|
import textwrap
|
|
import binascii
|
|
import string
|
|
from errno import EEXIST, EIO
|
|
|
|
import ps2mc
|
|
import ps2save
|
|
from ps2mc_dir import *
|
|
from round import *
|
|
import verbuild
|
|
|
|
class subopt_error(Exception):
|
|
pass
|
|
|
|
io_error = ps2mc.io_error
|
|
|
|
if os.name == "nt":
|
|
import codecs
|
|
|
|
class file_wrap(object):
|
|
""" wrap a file-like object with a new encoding attribute. """
|
|
|
|
def __init__(self, f, encoding):
|
|
object.__setattr__(self, "_f", f)
|
|
object.__setattr__(self, "encoding", encoding)
|
|
|
|
def __getattribute__(self, name):
|
|
if name == "encoding":
|
|
return object.__getattribute__(self, name)
|
|
return getattr(object.__getattribute__(self, "_f"),
|
|
name)
|
|
|
|
def __setattr__(self, name, value):
|
|
if name == "encoding":
|
|
raise TypeError, "readonly attribute"
|
|
return setattr(object.__getattribute__(self, "_f"),
|
|
name, value)
|
|
|
|
for name in ["stdin", "stdout", "stderr"]:
|
|
f = getattr(sys, name)
|
|
cur = getattr(f, "encoding", None)
|
|
if cur == "ascii" or cur == None:
|
|
f = file_wrap(f, "mbcs")
|
|
else:
|
|
try:
|
|
codecs.lookup(cur)
|
|
except LookupError:
|
|
f = file_wrap(f, "mbcs")
|
|
setattr(sys, name, f)
|
|
|
|
|
|
if os.name in ["nt", "os2", "ce"]:
|
|
from glob import glob
|
|
else:
|
|
# assume globing is done by the shell
|
|
glob = lambda pattern: [pattern]
|
|
|
|
|
|
def glob_args(args, globfn):
|
|
ret = []
|
|
for arg in args:
|
|
match = globfn(arg)
|
|
if len(match) == 0:
|
|
ret.append(arg)
|
|
else:
|
|
ret += match
|
|
return ret
|
|
|
|
def _copy(fout, fin):
|
|
"""copy the contents of one file to another"""
|
|
|
|
while True:
|
|
s = fin.read(1024)
|
|
if s == "":
|
|
break
|
|
fout.write(s)
|
|
|
|
|
|
def do_ls(cmd, mc, opts, args, opterr):
|
|
mode_bits = "rwxpfdD81C+KPH4"
|
|
|
|
if len(args) == 0:
|
|
args = ["/"]
|
|
|
|
out = sys.stdout
|
|
args = glob_args(args, mc.glob)
|
|
for dirname in args:
|
|
dir = mc.dir_open(dirname)
|
|
try:
|
|
if len(args) > 1:
|
|
sys.stdout.write("\n" + dirname + ":\n")
|
|
for ent in dir:
|
|
mode = ent[0]
|
|
if (mode & DF_EXISTS) == 0:
|
|
continue
|
|
for bit in range(0, 15):
|
|
if mode & (1 << bit):
|
|
out.write(mode_bits[bit])
|
|
else:
|
|
out.write("-")
|
|
if opts.creation_time:
|
|
tod = ent[3]
|
|
else:
|
|
tod = ent[6]
|
|
tm = time.localtime(tod_to_time(tod))
|
|
out.write(" %7d %04d-%02d-%02d"
|
|
" %02d:%02d:%02d %s\n"
|
|
% (ent[2],
|
|
tm.tm_year, tm.tm_mon, tm.tm_mday,
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec,
|
|
ent[8]))
|
|
finally:
|
|
dir.close()
|
|
|
|
|
|
def do_add(cmd, mc, opts, args, opterr):
|
|
if len(args) < 1:
|
|
opterr("Filename required.")
|
|
if opts.directory != None:
|
|
mc.chdir(opts.directory)
|
|
for src in glob_args(args, glob):
|
|
f = open(src, "rb")
|
|
dest = os.path.basename(src)
|
|
out = mc.open(dest, "wb")
|
|
_copy(out, f)
|
|
out.close()
|
|
f.close()
|
|
|
|
def do_extract(cmd, mc, opts, args, opterr):
|
|
if len(args) < 1:
|
|
opterr("Filename required.")
|
|
|
|
if opts.directory != None:
|
|
mc.chdir(opts.directory)
|
|
|
|
close_out = False
|
|
out = None
|
|
if opts.output != None:
|
|
if opts.use_stdout:
|
|
opterr("The -o and -p options are mutually exclusive.")
|
|
dont_close_out = True
|
|
out = file(opts.output, "wb")
|
|
elif opts.use_stdout:
|
|
out = sys.stdout
|
|
|
|
try:
|
|
for filename in glob_args(args, mc.glob):
|
|
f = mc.open(filename, "rb")
|
|
try:
|
|
if out != None:
|
|
_copy(out, f)
|
|
continue
|
|
a = filename.split("/")
|
|
o = file(a[-1], "wb")
|
|
try:
|
|
_copy(o, f)
|
|
finally:
|
|
o.close()
|
|
finally:
|
|
f.close()
|
|
finally:
|
|
if close_out:
|
|
out.close()
|
|
|
|
def do_mkdir(cmd, mc, opts, args, opterr):
|
|
if len(args) < 1:
|
|
opterr("Directory required.")
|
|
|
|
for filename in args:
|
|
mc.mkdir(filename)
|
|
|
|
def do_remove(cmd, mc, opts, args, opterr):
|
|
if len(args) < 1:
|
|
opterr("Filename required.")
|
|
|
|
for filename in args:
|
|
mc.remove(filename)
|
|
|
|
def do_import(cmd, mc, opts, args, opterr):
|
|
if len(args) < 1:
|
|
opterr("Filename required.")
|
|
|
|
args = glob_args(args, glob)
|
|
if opts.directory != None and len(args) > 1:
|
|
opterr("The -d option can only be used with a"
|
|
"single savefile.")
|
|
|
|
for filename in args:
|
|
sf = ps2save.ps2_save_file()
|
|
f = file(filename, "rb")
|
|
try:
|
|
ftype = ps2save.detect_file_type(f)
|
|
f.seek(0)
|
|
if ftype == "max":
|
|
sf.load_max_drive(f)
|
|
elif ftype == "psu":
|
|
sf.load_ems(f)
|
|
elif ftype == "cbs":
|
|
sf.load_codebreaker(f)
|
|
elif ftype == "sps":
|
|
sf.load_sharkport(f)
|
|
elif ftype == "npo":
|
|
raise io_error, (EIO, "nPort saves"
|
|
" are not supported.",
|
|
filename)
|
|
else:
|
|
raise io_error, (EIO, "Save file format not"
|
|
" recognized", filename)
|
|
finally:
|
|
f.close()
|
|
dirname = opts.directory
|
|
if dirname == None:
|
|
dirname = sf.get_directory()[8]
|
|
print "Importing", filename, "to", dirname
|
|
if not mc.import_save_file(sf, opts.ignore_existing,
|
|
opts.directory):
|
|
print (filename + ": already in memory card image,"
|
|
" ignored.")
|
|
|
|
#re_num = re.compile("[0-9]+")
|
|
|
|
def do_export(cmd, mc, opts, args, opterr):
|
|
if len(args) < 1:
|
|
opterr("Directory name required")
|
|
|
|
if opts.overwrite_existing and opts.ignore_existing:
|
|
opterr("The -i and -f options are mutually exclusive.")
|
|
|
|
args = glob_args(args, mc.glob)
|
|
if opts.output_file != None:
|
|
if len(args) > 1:
|
|
opterr("Only one directory can be exported"
|
|
" when the -o option is used.")
|
|
if opts.longnames:
|
|
opterr("The -o and -l options are mutually exclusive.")
|
|
|
|
if opts.directory != None:
|
|
os.chdir(opts.directory)
|
|
|
|
for dirname in args:
|
|
sf = mc.export_save_file(dirname)
|
|
filename = opts.output_file
|
|
if opts.longnames:
|
|
filename = (ps2save.make_longname(dirname, sf)
|
|
+ "." + opts.type)
|
|
if filename == None:
|
|
filename = dirname + "." + opts.type
|
|
|
|
if not opts.overwrite_existing:
|
|
exists = True
|
|
try:
|
|
open(filename, "rb").close()
|
|
except EnvironmentError:
|
|
exists = False
|
|
if exists:
|
|
if opts.ignore_existing:
|
|
continue
|
|
raise io_error(EEXIST, "File exists", filename)
|
|
|
|
f = file(filename, "wb")
|
|
try:
|
|
print "Exporing", dirname, "to", filename
|
|
|
|
if opts.type == "max":
|
|
sf.save_max_drive(f)
|
|
else:
|
|
sf.save_ems(f)
|
|
finally:
|
|
f.close()
|
|
|
|
def do_delete(cmd, mc, opts, args, opterr):
|
|
if len(args) < 1:
|
|
opterr("Directory required.")
|
|
|
|
for dirname in args:
|
|
mc.rmdir(dirname)
|
|
|
|
def do_setmode(cmd, mc, opts, args, opterr):
|
|
set_mask = 0
|
|
clear_mask = ~0
|
|
for (opt, bit) in [(opts.read, DF_READ),
|
|
(opts.write, DF_WRITE),
|
|
(opts.execute, DF_EXECUTE),
|
|
(opts.protected, DF_PROTECTED),
|
|
(opts.psx, DF_PSX),
|
|
(opts.pocketstation, DF_POCKETSTN),
|
|
(opts.hidden, DF_HIDDEN)]:
|
|
if opt != None:
|
|
if opt:
|
|
set_mask |= bit
|
|
else:
|
|
clear_mask ^= bit
|
|
|
|
value = opts.hex_value
|
|
if set_mask == 0 and clear_mask == ~0:
|
|
if value == None:
|
|
opterr("At least one option must be given.")
|
|
if value.startswith("0x") or value.startswith("0X"):
|
|
value = int(value[2:], 16)
|
|
else:
|
|
value = int(value, 16)
|
|
else:
|
|
if value != None:
|
|
opterr("The -X option can't be combined with"
|
|
" other options.")
|
|
|
|
for arg in glob_args(args, mc.glob):
|
|
ent = mc.get_dirent(arg)
|
|
if value == None:
|
|
ent[0] = (ent[0] & clear_mask) | set_mask
|
|
# print "new %04x" % ent[0]
|
|
else:
|
|
ent[0] = value
|
|
mc.set_dirent(arg, ent)
|
|
|
|
def _get_ps2_title(mc, enc):
|
|
s = mc.get_icon_sys(".");
|
|
if s == None:
|
|
return None
|
|
a = ps2save.unpack_icon_sys(s)
|
|
return ps2save.icon_sys_title(a, enc)
|
|
|
|
def _get_psx_title(mc, savename, enc):
|
|
mode = mc.get_mode(savename)
|
|
if mode == None or not mode_is_file(mode):
|
|
return None
|
|
f = mc.open(savename)
|
|
s = f.read(128)
|
|
if len(s) != 128:
|
|
return None
|
|
(magic, icon, blocks, title) = struct.unpack("<2sBB64s28x32x", s)
|
|
if magic != "SC":
|
|
return None
|
|
return [ps2save.shift_jis_conv(zero_terminate(title), enc), ""]
|
|
|
|
def do_dir(cmd, mc, opts, args, opterr):
|
|
if len(args) != 0:
|
|
opterr("Incorrect number of arguments.")
|
|
f = None
|
|
dir = mc.dir_open("/")
|
|
try:
|
|
for ent in list(dir)[2:]:
|
|
dirmode = ent[0]
|
|
if not mode_is_dir(dirmode):
|
|
continue
|
|
dirname = "/" + ent[8]
|
|
mc.chdir(dirname)
|
|
length = mc.dir_size(".");
|
|
enc = getattr(sys.stdout, "encoding", None)
|
|
if dirmode & DF_PSX:
|
|
title = _get_psx_title(mc, ent[8], enc)
|
|
else:
|
|
title = _get_ps2_title(mc, enc)
|
|
if title == None:
|
|
title = ["Corrupt", ""]
|
|
protection = dirmode & (DF_PROTECTED | DF_WRITE)
|
|
if protection == 0:
|
|
protection = "Delete Protected"
|
|
elif protection == DF_WRITE:
|
|
protection = "Not Protected"
|
|
elif protection == DF_PROTECTED:
|
|
protection = "Copy & Delete Protected"
|
|
else:
|
|
protection = "Copy Protected"
|
|
|
|
type = None
|
|
if dirmode & DF_PSX:
|
|
type = "PlayStation"
|
|
if dirmode & DF_POCKETSTN:
|
|
type = "PocketStation"
|
|
if type != None:
|
|
protection = type
|
|
|
|
print "%-32s %s" % (ent[8], title[0])
|
|
print ("%4dKB %-25s %s"
|
|
% (length / 1024, protection, title[1]))
|
|
print
|
|
finally:
|
|
if f != None:
|
|
f.close()
|
|
dir.close()
|
|
|
|
free = mc.get_free_space() / 1024
|
|
if free > 999999:
|
|
free = "%d,%03d,%03d" % (free / 1000000, free / 1000 % 1000,
|
|
free % 1000)
|
|
elif free > 999:
|
|
free = "%d,%03d" % (free / 1000, free % 1000)
|
|
else:
|
|
free = "%d" % free
|
|
|
|
print free + " KB Free"
|
|
|
|
def do_df(cmd, mc, opts, args, opterr):
|
|
if len(args) != 0:
|
|
opterr("Incorrect number of arguments.")
|
|
print mc.f.name + ":", mc.get_free_space(), "bytes free."
|
|
|
|
def do_check(cmd, mc, opts, args, opterr):
|
|
if len(args) != 0:
|
|
opterr("Incorrect number of arguments.")
|
|
if mc.check():
|
|
print "No errors found."
|
|
return 0
|
|
return 1
|
|
|
|
def do_format(cmd, mcname, opts, args, opterr):
|
|
if len(args) != 0:
|
|
opterr("Incorrect number of arguments.")
|
|
pages_per_card = ps2mc.PS2MC_STANDARD_PAGES_PER_CARD
|
|
if opts.clusters != None:
|
|
pages_per_cluster = (ps2mc.PS2MC_CLUSTER_SIZE
|
|
/ ps2mc.PS2MC_STANDARD_PAGE_SIZE)
|
|
pages_per_card = opts.clusters * pages_per_cluster
|
|
params = (not opts.no_ecc,
|
|
ps2mc.PS2MC_STANDARD_PAGE_SIZE,
|
|
ps2mc.PS2MC_STANDARD_PAGES_PER_ERASE_BLOCK,
|
|
pages_per_card)
|
|
|
|
if not opts.overwrite_existing:
|
|
exists = True
|
|
try:
|
|
file(mcname, "rb").close()
|
|
except EnvironmentError:
|
|
exists = False
|
|
if exists:
|
|
raise io_error, (EEXIST, "file exists", mcname)
|
|
|
|
f = file(mcname, "w+b")
|
|
try:
|
|
ps2mc.ps2mc(f, True, params).close()
|
|
finally:
|
|
f.close()
|
|
|
|
def do_gui(cmd, mcname, opts, args, opterr):
|
|
if len(args) != 0:
|
|
opterr("Incorrect number of arguments.")
|
|
|
|
try:
|
|
import gui
|
|
except ImportError:
|
|
write_error(None, "GUI not available")
|
|
return 1
|
|
|
|
gui.run(mcname)
|
|
return 0
|
|
|
|
def do_create_pad(cmd, mc, opts, args, opterr):
|
|
length = mc.clusters_per_card
|
|
if len(args) > 1:
|
|
length = int(args[1])
|
|
pad = "\0" * mc.cluster_size
|
|
f = mc.open(args[0], "wb")
|
|
try:
|
|
for i in xrange(length):
|
|
f.write(pad)
|
|
finally:
|
|
f.close()
|
|
|
|
|
|
def do_frob(cmd, mc, opts, args, opterr):
|
|
mc.write_superblock()
|
|
|
|
_trans = string.maketrans("".join(map(chr, range(32))), " " * 32)
|
|
|
|
def _print_bin(base, s):
|
|
for off in range(0, len(s), 16):
|
|
print "%04X" % (base + off),
|
|
a = s[off : off + 16]
|
|
for b in a:
|
|
print "%02X" % ord(b),
|
|
print "", a.translate(_trans)
|
|
|
|
def _print_erase_block(mc, n):
|
|
ppb = mc.pages_per_erase_block
|
|
base = n * ppb
|
|
for i in range(ppb):
|
|
s = mc.read_page(base + i)
|
|
_print_bin(i * mc.page_size, s)
|
|
print
|
|
|
|
def do_print_good_blocks(cmd, mc, opts, args, opterr):
|
|
print "good_block2:"
|
|
_print_erase_block(mc, mc.good_block2)
|
|
print "good_block1:"
|
|
_print_erase_block(mc, mc.good_block1)
|
|
|
|
def do_ecc_check(cmd, mc, opts, args, opterr):
|
|
for i in range(mc.clusters_per_card * mc.pages_per_cluster):
|
|
try:
|
|
mc.read_page(i)
|
|
except ps2mc.ecc_error:
|
|
print "bad: %05x" % i
|
|
|
|
opt = optparse.make_option
|
|
|
|
#
|
|
# Each value in the dictionary is a tuple consisting of:
|
|
# - function implementing the command
|
|
# - mode to use to open the ps2 save file
|
|
# - help description of the command
|
|
# - list of options supported by the command
|
|
#
|
|
cmd_table = {
|
|
"ls": (do_ls, "rb",
|
|
"[directory ...]",
|
|
"List the contents of a directory.",
|
|
[opt("-c", "--creation-time", action="store_true",
|
|
help = "Display creation times.")]),
|
|
"extract": (do_extract, "rb",
|
|
"filename ...",
|
|
"Extract files from the memory card.",
|
|
[opt("-o", "--output", metavar = "FILE",
|
|
help = 'Extract file to "FILE".'),
|
|
opt("-d", "--directory",
|
|
help = 'Extract files from "DIRECTORY".'),
|
|
opt("-p", "--use-stdout", action="store_true",
|
|
help = "Extract files to standard output.")]),
|
|
"add": (do_add, "r+b",
|
|
"filename ...",
|
|
"Add files to the memory card.",
|
|
[opt("-d", "--directory",
|
|
help = 'Add files to "directory".')]),
|
|
"mkdir": (do_mkdir, "r+b",
|
|
"directory ...",
|
|
"Make directories.",
|
|
[]),
|
|
"remove": (do_remove, "r+b",
|
|
"filename ...",
|
|
"Remove files and directories.",
|
|
[]),
|
|
"import": (do_import, "r+b",
|
|
"savefile ...",
|
|
"Import save files into the memory card.",
|
|
[opt("-i", "--ignore-existing", action="store_true",
|
|
help = ("Ignore files that already exist"
|
|
"on the image.")),
|
|
opt("-d", "--directory", metavar="DEST",
|
|
help = 'Import to "DEST".')]),
|
|
"export": (do_export, "rb",
|
|
"directory ...",
|
|
"Export save files from the memory card.",
|
|
[opt("-f", "--overwrite-existing", action = "store_true",
|
|
help = "Overwrite any save files already exported."),
|
|
opt("-i", "--ignore-existing", action = "store_true",
|
|
help = "Ingore any save files already exported."),
|
|
opt("-o", "--output-file", metavar = "filename",
|
|
help = 'Use "filename" as the name of the save file.'),
|
|
opt("-d", "--directory", metavar = "directory",
|
|
help = 'Export save files to "directory".'),
|
|
opt("-l", "--longnames", action = "store_true",
|
|
help = ("Generate longer, more descriptive,"
|
|
" filenames.")),
|
|
opt("-p", "--ems", action = "store_const",
|
|
dest = "type", const = "psu", default = "psu",
|
|
help = "Use the EMS .psu save file format. [default]"),
|
|
opt("-m", "--max-drive", action = "store_const",
|
|
dest = "type", const = "max",
|
|
help = "Use the MAX Drive save file format.")]),
|
|
"delete": (do_delete, "r+b",
|
|
"dirname ...",
|
|
"Recursively delete a directory (save file).",
|
|
[]),
|
|
"set": (do_setmode, "r+b",
|
|
"filename ...",
|
|
"Set mode flags on files and directories",
|
|
[opt("-p", "--protected", action="store_true",
|
|
help = "Set copy protected flag"),
|
|
opt("-P", "--psx", action="store_true",
|
|
help = "Set PSX flag"),
|
|
opt("-K", "--pocketstation", action="store_true",
|
|
help = "Set PocketStation flag"),
|
|
opt("-H", "--hidden", action="store_true",
|
|
help = "Set hidden flag"),
|
|
opt("-r", "--read", action="store_true",
|
|
help = "Set read allowed flag"),
|
|
opt("-w", "--write", action="store_true",
|
|
help = "Set write allowed flag"),
|
|
opt("-x", "--execute", action="store_true",
|
|
help = "Set executable flag"),
|
|
opt("-X", "--hex-value", metavar="mode",
|
|
help = 'Set mode to "mode".')]),
|
|
"clear": (do_setmode, "r+b",
|
|
"filename ...",
|
|
"Clear mode flags on files and directories",
|
|
[opt("-p", "--protected", action="store_false",
|
|
help = "Clear copy protected flag"),
|
|
opt("-P", "--psx", action="store_false",
|
|
help = "Clear PSX flag"),
|
|
opt("-K", "--pocketstation", action="store_false",
|
|
help = "Clear PocketStation flag"),
|
|
opt("-H", "--hidden", action="store_false",
|
|
help = "Clear hidden flag"),
|
|
opt("-r", "--read", action="store_false",
|
|
help = "Clear read allowed flag"),
|
|
opt("-w", "--write", action="store_false",
|
|
help = "Clear write allowed flag"),
|
|
opt("-x", "--execute", action="store_false",
|
|
help = "Clear executable flag"),
|
|
opt("-X", dest="hex_value", default=None,
|
|
help = optparse.SUPPRESS_HELP)]),
|
|
"dir": (do_dir, "rb",
|
|
None,
|
|
"Display save file information.",
|
|
[]),
|
|
"df": (do_df, "rb",
|
|
None,
|
|
"Display the amount free space.",
|
|
[]),
|
|
"check": (do_check, "rb",
|
|
"",
|
|
"Check for file system errors.",
|
|
[]),
|
|
"format": (do_format, None,
|
|
"",
|
|
"Creates a new memory card image.",
|
|
[opt("-c", "--clusters", type="int",
|
|
help = "Size in clusters of the memory card."),
|
|
opt("-f", "--overwrite-existing", action="store_true",
|
|
help = "Overwrite any existing file"),
|
|
opt("-e", "--no-ecc", action="store_true",
|
|
help = "Create an image without ECC")]),
|
|
"gui": (do_gui, None,
|
|
"",
|
|
"Starts the graphical user interface.",
|
|
[]),
|
|
}
|
|
|
|
#
|
|
# secret commands for debugging purposes.
|
|
#
|
|
debug_cmd_table = {
|
|
"frob": (do_frob, "r+b",
|
|
"",
|
|
None,
|
|
[]),
|
|
"print_good_blocks": (do_print_good_blocks, "rb",
|
|
"",
|
|
None,
|
|
[]),
|
|
"ecc_check": (do_ecc_check, "rb",
|
|
"",
|
|
None,
|
|
[]),
|
|
"create_pad": (do_create_pad, "r+b",
|
|
"",
|
|
None,
|
|
[])
|
|
}
|
|
|
|
del opt # clean up name space
|
|
|
|
|
|
def write_error(filename, msg):
|
|
if filename == None:
|
|
sys.stderr.write(msg + "\n")
|
|
else:
|
|
sys.stderr.write(filename + ": " + msg + "\n")
|
|
|
|
class suboption_parser(optparse.OptionParser):
|
|
def exit(self, status = 0, msg = None):
|
|
if msg:
|
|
sys.stderr.write(msg)
|
|
raise subopt_error, status
|
|
|
|
class my_help_formatter(optparse.IndentedHelpFormatter):
|
|
"""A better formatter for optparser's help message"""
|
|
|
|
def format_description(self, description):
|
|
if not description:
|
|
return ""
|
|
desc_width = self.width - self.current_indent
|
|
indent = " " * self.current_indent
|
|
lines = []
|
|
for line in description.split('\n'):
|
|
ii = indent
|
|
si = indent
|
|
if line.startswith("\t"):
|
|
line = line[1:]
|
|
ii = indent + " " * 4
|
|
si = ii + " " * line.find(":") + 2
|
|
line = textwrap.fill(line, desc_width,
|
|
initial_indent = ii,
|
|
subsequent_indent = si)
|
|
lines.append(line)
|
|
return "\n".join(lines) + "\n"
|
|
|
|
def main():
|
|
prog = sys.argv[0].decode(sys.getdefaultencoding(), "replace")
|
|
usage = "usage: %prog [-ih] memcard.ps2 command [...]"
|
|
description = ("Manipulate PS2 memory card images.\n\n"
|
|
"Supported commands: ")
|
|
for cmd in sorted(cmd_table.keys()):
|
|
description += "\n " + cmd + ": " + cmd_table[cmd][3]
|
|
|
|
version = ("mymc "
|
|
+ verbuild.MYMC_VERSION_MAJOR
|
|
+ "." + verbuild.MYMC_VERSION_BUILD
|
|
+ " (" + _SCCS_ID + ")")
|
|
|
|
optparser = optparse.OptionParser(prog = prog, usage = usage,
|
|
description = description,
|
|
version = version,
|
|
formatter = my_help_formatter())
|
|
optparser.add_option("-D", dest = "debug", action = "store_true",
|
|
default = False, help = optparse.SUPPRESS_HELP)
|
|
optparser.add_option("-i", "--ignore-ecc", action = "store_true",
|
|
help = "Ignore ECC errors while reading.")
|
|
|
|
optparser.disable_interspersed_args()
|
|
(opts, args) = optparser.parse_args()
|
|
|
|
if len(args) == 0:
|
|
try:
|
|
import gui
|
|
except ImportError:
|
|
gui = None
|
|
if gui != None:
|
|
gui.run()
|
|
sys.exit(0)
|
|
|
|
if len(args) < 2:
|
|
optparser.error("Incorrect number of arguments.")
|
|
|
|
if opts.debug:
|
|
cmd_table.update(debug_cmd_table)
|
|
cmd = args[1]
|
|
if cmd not in cmd_table:
|
|
optparser.error('Command "%s" not recognized.' % cmd)
|
|
(fn, mode, usage_args, description, optlist) = cmd_table[cmd]
|
|
|
|
usage = "%prog"
|
|
if len(optlist) > 0:
|
|
usage += " [options]"
|
|
if usage_args != None:
|
|
usage += " " + usage_args
|
|
subprog = prog + " memcard.ps2 " + cmd
|
|
subopt_parser = suboption_parser(prog = subprog, usage = usage,
|
|
description = description,
|
|
option_list = optlist)
|
|
subopt_parser.disable_interspersed_args()
|
|
|
|
f = None
|
|
mc = None
|
|
ret = 0
|
|
mcname = args[0]
|
|
|
|
try:
|
|
(subopts, subargs) = subopt_parser.parse_args(args[2:])
|
|
try:
|
|
if mode == None:
|
|
ret = fn(cmd, mcname, subopts, subargs,
|
|
subopt_parser.error)
|
|
else:
|
|
f = file(mcname, mode)
|
|
mc = ps2mc.ps2mc(f, opts.ignore_ecc)
|
|
ret = fn(cmd, mc, subopts, subargs,
|
|
subopt_parser.error)
|
|
finally:
|
|
if mc != None:
|
|
mc.close()
|
|
if f != None:
|
|
# print "f.close()"
|
|
f.close()
|
|
|
|
except EnvironmentError, value:
|
|
if getattr(value, "filename", None) != None:
|
|
write_error(value.filename, value.strerror)
|
|
ret = 1
|
|
elif getattr(value, "strerror", None) != None:
|
|
write_error(mcname, value.strerror)
|
|
ret = 1
|
|
else:
|
|
# something weird
|
|
raise
|
|
if opts.debug:
|
|
raise
|
|
|
|
except subopt_error, (ret,):
|
|
pass
|
|
|
|
except (ps2mc.error, ps2save.error), value:
|
|
fn = getattr(value, "filename", None)
|
|
if fn == None:
|
|
fn = mcname
|
|
write_error(fn, str(value))
|
|
if opts.debug:
|
|
raise
|
|
ret = 1
|
|
|
|
if ret == None:
|
|
ret = 0
|
|
|
|
return ret
|
|
|
|
sys.exit(main())
|
|
|