mirror of
https://github.com/ps2dev/mymc.git
synced 2024-11-30 13:24:27 +01:00
949 lines
25 KiB
Python
949 lines
25 KiB
Python
|
#
|
||
|
# gui.py
|
||
|
#
|
||
|
# By Ross Ridge
|
||
|
# Public Domain
|
||
|
#
|
||
|
|
||
|
"""Graphical user-interface for mymc."""
|
||
|
|
||
|
_SCCS_ID = "@(#) mymc gui.py 1.3 08/02/06 13:15:41\n"
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
import struct
|
||
|
import cStringIO
|
||
|
import time
|
||
|
|
||
|
# Work around a problem with mixing wx and py2exe
|
||
|
if os.name == "nt" and hasattr(sys, "setdefaultencoding"):
|
||
|
sys.setdefaultencoding("mbcs")
|
||
|
import wx
|
||
|
|
||
|
import ps2mc
|
||
|
import ps2save
|
||
|
import guires
|
||
|
|
||
|
try:
|
||
|
import ctypes
|
||
|
import mymcsup
|
||
|
D3DXVECTOR3 = mymcsup.D3DXVECTOR3
|
||
|
D3DXVECTOR4 = mymcsup.D3DXVECTOR4
|
||
|
D3DXVECTOR4_ARRAY3 = mymcsup.D3DXVECTOR4_ARRAY3
|
||
|
|
||
|
def mkvec4arr3(l):
|
||
|
return D3DXVECTOR4_ARRAY3(*[D3DXVECTOR4(*vec)
|
||
|
for vec in l])
|
||
|
except ImportError:
|
||
|
mymcsup = None
|
||
|
|
||
|
lighting_none = {"lighting": False,
|
||
|
"vertex_diffuse": False,
|
||
|
"alt_lighting": False,
|
||
|
"light_dirs": [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
|
||
|
"light_colours": [[0, 0, 0, 0], [0, 0, 0, 0],
|
||
|
[0, 0, 0, 0]],
|
||
|
"ambient": [0, 0, 0, 0]}
|
||
|
|
||
|
lighting_diffuse = {"lighting": False,
|
||
|
"vertex_diffuse": True,
|
||
|
"alt_lighting": False,
|
||
|
"light_dirs": [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
|
||
|
"light_colours": [[0, 0, 0, 0], [0, 0, 0, 0],
|
||
|
[0, 0, 0, 0]],
|
||
|
"ambient": [0, 0, 0, 0]}
|
||
|
|
||
|
lighting_icon = {"lighting": True,
|
||
|
"vertex_diffuse": True,
|
||
|
"alt_lighting": False,
|
||
|
"light_dirs": [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
|
||
|
"light_colours": [[0, 0, 0, 0], [0, 0, 0, 0],
|
||
|
[0, 0, 0, 0]],
|
||
|
"ambient": [0, 0, 0, 0]}
|
||
|
|
||
|
lighting_alternate = {"lighting": True,
|
||
|
"vertex_diffuse": True,
|
||
|
"alt_lighting": True,
|
||
|
"light_dirs": [[1, -1, 2, 0],
|
||
|
[-1, 1, -2, 0],
|
||
|
[0, 1, 0, 0]],
|
||
|
"light_colours": [[1, 1, 1, 1],
|
||
|
[1, 1, 1, 1],
|
||
|
[0.7, 0.7, 0.7, 1]],
|
||
|
"ambient": [0.5, 0.5, 0.5, 1]}
|
||
|
|
||
|
lighting_alternate2 = {"lighting": True,
|
||
|
"vertex_diffuse": False,
|
||
|
"alt_lighting": True,
|
||
|
"light_dirs": [[1, -1, 2, 0],
|
||
|
[-1, 1, -2, 0],
|
||
|
[0, 4, 1, 0]],
|
||
|
"light_colours": [[0.7, 0.7, 0.7, 1],
|
||
|
[0.7, 0.7, 0.7, 1],
|
||
|
[0.2, 0.2, 0.2, 1]],
|
||
|
"ambient": [0.3, 0.3, 0.3, 1]}
|
||
|
|
||
|
camera_default = [0, 4, -8]
|
||
|
camera_high = [0, 7, -6]
|
||
|
camera_near = [0, 3, -6]
|
||
|
camera_flat = [0, 2, -7.5]
|
||
|
|
||
|
def get_dialog_units(win):
|
||
|
return win.ConvertDialogPointToPixels((1, 1))[0]
|
||
|
|
||
|
def single_title(title):
|
||
|
"""Convert the two parts of an icon.sys title into one string."""
|
||
|
|
||
|
title = title[0] + " " + title[1]
|
||
|
return u" ".join(title.split())
|
||
|
|
||
|
def _get_icon_resource_as_images(name):
|
||
|
ico = guires.resources[name]
|
||
|
images = []
|
||
|
f = cStringIO.StringIO(ico)
|
||
|
count = struct.unpack("<HHH", ico[0:6])[2]
|
||
|
# count = wx.Image_GetImageCount(f, wx.BITMAP_TYPE_ICO)
|
||
|
for i in range(count):
|
||
|
f.seek(0)
|
||
|
images.append(wx.ImageFromStream(f, wx.BITMAP_TYPE_ICO, i))
|
||
|
return images
|
||
|
|
||
|
def get_icon_resource(name):
|
||
|
"""Convert a Window ICO contained in a string to an IconBundle."""
|
||
|
|
||
|
bundle = wx.IconBundle()
|
||
|
for img in _get_icon_resource_as_images(name):
|
||
|
bmp = wx.BitmapFromImage(img)
|
||
|
icon = wx.IconFromBitmap(bmp)
|
||
|
bundle.AddIcon(icon)
|
||
|
return bundle
|
||
|
|
||
|
def get_icon_resource_bmp(name, size):
|
||
|
"""Get an icon resource as a Bitmap.
|
||
|
|
||
|
Tries to find the closest matching size if no exact match exists."""
|
||
|
|
||
|
best = None
|
||
|
best_size = (0, 0)
|
||
|
for img in _get_icon_resource_as_images(name):
|
||
|
sz = (img.GetWidth(), img.GetHeight())
|
||
|
if sz == size:
|
||
|
return wx.BitmapFromImage(img)
|
||
|
if sz[0] >= size[0] and sz[1] >= size[1]:
|
||
|
if ((best_size[0] < size[0] or best_size[1] < size[1])
|
||
|
or sz[0] * sz[1] < best_size[0] * best_size[1]):
|
||
|
best = img
|
||
|
best_size = sz
|
||
|
elif sz[0] * sz[1] > best_size[0] * best_size[1]:
|
||
|
best = img
|
||
|
best_size = sz
|
||
|
img = best.Rescale(size[0], size[1], wx.IMAGE_QUALITY_HIGH)
|
||
|
return wx.BitmapFromImage(img)
|
||
|
|
||
|
|
||
|
class dirlist_control(wx.ListCtrl):
|
||
|
"""Lists all the save files in a memory card image."""
|
||
|
|
||
|
def __init__(self, parent, evt_focus, evt_select, config):
|
||
|
self.config = config
|
||
|
self.selected = set()
|
||
|
self.evt_select = evt_select
|
||
|
wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
|
||
|
style = wx.LC_REPORT)
|
||
|
wx.EVT_LIST_COL_CLICK(self, -1, self.evt_col_click)
|
||
|
wx.EVT_LIST_ITEM_FOCUSED(self, -1, evt_focus)
|
||
|
wx.EVT_LIST_ITEM_SELECTED(self, -1, self.evt_item_selected)
|
||
|
wx.EVT_LIST_ITEM_DESELECTED(self, -1, self.evt_item_deselected)
|
||
|
|
||
|
def _update_dirtable(self, mc, dir):
|
||
|
self.dirtable = table = []
|
||
|
enc = "unicode"
|
||
|
if self.config.get_ascii():
|
||
|
enc = "ascii"
|
||
|
for ent in dir:
|
||
|
if not ps2mc.mode_is_dir(ent[0]):
|
||
|
continue
|
||
|
dirname = "/" + ent[8]
|
||
|
s = mc.get_icon_sys(dirname)
|
||
|
if s == None:
|
||
|
continue
|
||
|
a = ps2save.unpack_icon_sys(s)
|
||
|
size = mc.dir_size(dirname)
|
||
|
title = ps2save.icon_sys_title(a, encoding = enc)
|
||
|
table.append((ent, s, size, title))
|
||
|
|
||
|
def update_dirtable(self, mc):
|
||
|
self.dirtable = []
|
||
|
if mc == None:
|
||
|
return
|
||
|
dir = mc.dir_open("/")
|
||
|
try:
|
||
|
self._update_dirtable(mc, dir)
|
||
|
finally:
|
||
|
dir.close()
|
||
|
|
||
|
def cmp_dir_name(self, i1, i2):
|
||
|
return self.dirtable[i1][0][8] > self.dirtable[i2][0][8]
|
||
|
|
||
|
def cmp_dir_title(self, i1, i2):
|
||
|
return self.dirtable[i1][3] > self.dirtable[i2][3]
|
||
|
|
||
|
def cmp_dir_size(self, i1, i2):
|
||
|
return self.dirtable[i1][2] > self.dirtable[i2][2]
|
||
|
|
||
|
def cmp_dir_modified(self, i1, i2):
|
||
|
m1 = list(self.dirtable[i1][0][6])
|
||
|
m2 = list(self.dirtable[i2][0][6])
|
||
|
m1.reverse()
|
||
|
m2.reverse()
|
||
|
return m1 > m2
|
||
|
|
||
|
def evt_col_click(self, event):
|
||
|
col = event.m_col
|
||
|
if col == 0:
|
||
|
cmp = self.cmp_dir_name
|
||
|
elif col == 1:
|
||
|
cmp = self.cmp_dir_size
|
||
|
elif col == 2:
|
||
|
cmp = self.cmp_dir_modified
|
||
|
elif col == 3:
|
||
|
cmp = self.cmp_dir_title
|
||
|
self.SortItems(cmp)
|
||
|
return
|
||
|
|
||
|
def evt_item_selected(self, event):
|
||
|
self.selected.add(event.GetData())
|
||
|
self.evt_select(event)
|
||
|
|
||
|
def evt_item_deselected(self, event):
|
||
|
self.selected.discard(event.GetData())
|
||
|
self.evt_select(event)
|
||
|
|
||
|
def update(self, mc):
|
||
|
"""Update the ListCtrl according to the contents of the
|
||
|
memory card image."""
|
||
|
|
||
|
self.ClearAll()
|
||
|
self.selected = set()
|
||
|
self.InsertColumn(0, "Directory")
|
||
|
self.InsertColumn(1, "Size")
|
||
|
self.InsertColumn(2, "Modified")
|
||
|
self.InsertColumn(3, "Description")
|
||
|
li = self.GetColumn(1)
|
||
|
li.SetAlign(wx.LIST_FORMAT_RIGHT)
|
||
|
li.SetText("Size")
|
||
|
self.SetColumn(1, li)
|
||
|
|
||
|
self.update_dirtable(mc)
|
||
|
|
||
|
empty = len(self.dirtable) == 0
|
||
|
self.Enable(not empty)
|
||
|
if empty:
|
||
|
return
|
||
|
|
||
|
for (i, a) in enumerate(self.dirtable):
|
||
|
(ent, icon_sys, size, title) = a
|
||
|
li = self.InsertStringItem(i, ent[8])
|
||
|
self.SetStringItem(li, 1, "%dK" % (size / 1024))
|
||
|
m = ent[6]
|
||
|
m = ("%04d-%02d-%02d %02d:%02d"
|
||
|
% (m[5], m[4], m[3], m[2], m[1]))
|
||
|
self.SetStringItem(li, 2, m)
|
||
|
self.SetStringItem(li, 3, single_title(title))
|
||
|
self.SetItemData(li, i)
|
||
|
|
||
|
du = get_dialog_units(self)
|
||
|
for i in range(4):
|
||
|
self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
|
||
|
self.SetColumnWidth(i, self.GetColumnWidth(i) + du)
|
||
|
self.SortItems(self.cmp_dir_name)
|
||
|
|
||
|
|
||
|
class icon_window(wx.Window):
|
||
|
"""Displays a save file's 3D icon. Windows only.
|
||
|
|
||
|
The rendering of the 3D icon is handled by C++ code in the
|
||
|
mymcsup DLL which subclasses this window. This class mainly
|
||
|
handles configuration options that affect how the 3D icon is
|
||
|
displayed.
|
||
|
"""
|
||
|
|
||
|
ID_CMD_ANIMATE = 201
|
||
|
ID_CMD_LIGHT_NONE = 202
|
||
|
ID_CMD_LIGHT_ICON = 203
|
||
|
ID_CMD_LIGHT_ALT1 = 204
|
||
|
ID_CMD_LIGHT_ALT2 = 205
|
||
|
ID_CMD_CAMERA_FLAT = 206
|
||
|
ID_CMD_CAMERA_DEFAULT = 207
|
||
|
ID_CMD_CAMERA_NEAR = 209
|
||
|
ID_CMD_CAMERA_HIGH = 210
|
||
|
|
||
|
light_options = {ID_CMD_LIGHT_NONE: lighting_none,
|
||
|
ID_CMD_LIGHT_ICON: lighting_icon,
|
||
|
ID_CMD_LIGHT_ALT1: lighting_alternate,
|
||
|
ID_CMD_LIGHT_ALT2: lighting_alternate2}
|
||
|
|
||
|
camera_options = {ID_CMD_CAMERA_FLAT: camera_flat,
|
||
|
ID_CMD_CAMERA_DEFAULT: camera_default,
|
||
|
ID_CMD_CAMERA_NEAR: camera_near,
|
||
|
ID_CMD_CAMERA_HIGH: camera_high}
|
||
|
|
||
|
def append_menu_options(self, win, menu):
|
||
|
menu.AppendCheckItem(icon_window.ID_CMD_ANIMATE,
|
||
|
"Animate Icons")
|
||
|
menu.AppendSeparator()
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_LIGHT_NONE,
|
||
|
"Lighting Off")
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_LIGHT_ICON,
|
||
|
"Icon Lighting")
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_LIGHT_ALT1,
|
||
|
"Alternate Lighting")
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_LIGHT_ALT2,
|
||
|
"Alternate Lighting 2")
|
||
|
menu.AppendSeparator()
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_CAMERA_FLAT,
|
||
|
"Camera Flat")
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_CAMERA_DEFAULT,
|
||
|
"Camera Default")
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_CAMERA_NEAR,
|
||
|
"Camera Near")
|
||
|
menu.AppendRadioItem(icon_window.ID_CMD_CAMERA_HIGH,
|
||
|
"Camera High")
|
||
|
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_ANIMATE,
|
||
|
self.evt_menu_animate)
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_LIGHT_NONE,
|
||
|
self.evt_menu_light)
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_LIGHT_ICON,
|
||
|
self.evt_menu_light)
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_LIGHT_ALT1,
|
||
|
self.evt_menu_light)
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_LIGHT_ALT2,
|
||
|
self.evt_menu_light)
|
||
|
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_CAMERA_FLAT,
|
||
|
self.evt_menu_camera)
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_CAMERA_DEFAULT,
|
||
|
self.evt_menu_camera)
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_CAMERA_NEAR,
|
||
|
self.evt_menu_camera)
|
||
|
wx.EVT_MENU(win, icon_window.ID_CMD_CAMERA_HIGH,
|
||
|
self.evt_menu_camera)
|
||
|
|
||
|
def __init__(self, parent, focus):
|
||
|
self.failed = False
|
||
|
wx.Window.__init__(self, parent)
|
||
|
if mymcsup == None:
|
||
|
self.failed = True
|
||
|
return
|
||
|
r = mymcsup.init_icon_renderer(focus.GetHandle(),
|
||
|
self.GetHandle())
|
||
|
if r == -1:
|
||
|
print "init_icon_renderer failed"
|
||
|
self.failed = True
|
||
|
return
|
||
|
|
||
|
self.config = config = mymcsup.icon_config()
|
||
|
config.animate = True
|
||
|
|
||
|
self.menu = wx.Menu()
|
||
|
self.append_menu_options(self, self.menu)
|
||
|
self.set_lighting(self.ID_CMD_LIGHT_ALT2)
|
||
|
self.set_camera(self.ID_CMD_CAMERA_DEFAULT)
|
||
|
|
||
|
wx.EVT_CONTEXT_MENU(self, self.evt_context_menu)
|
||
|
|
||
|
def __del__(self):
|
||
|
if mymcsup != None:
|
||
|
mymcsup.delete_icon_renderer()
|
||
|
|
||
|
def update_menu(self, menu):
|
||
|
"""Update the content menu according to the current config."""
|
||
|
|
||
|
menu.Check(icon_window.ID_CMD_ANIMATE, self.config.animate)
|
||
|
menu.Check(self.lighting_id, True)
|
||
|
menu.Check(self.camera_id, True)
|
||
|
|
||
|
def load_icon(self, icon_sys, icon):
|
||
|
"""Pass the raw icon data to the support DLL for display."""
|
||
|
|
||
|
if self.failed:
|
||
|
return
|
||
|
|
||
|
if icon_sys == None or icon == None:
|
||
|
r = mymcsup.load_icon(None, 0, None, 0)
|
||
|
else:
|
||
|
r = mymcsup.load_icon(icon_sys, len(icon_sys),
|
||
|
icon, len(icon))
|
||
|
if r != 0:
|
||
|
print "load_icon", r
|
||
|
self.failed = True
|
||
|
|
||
|
def _set_lighting(self, lighting, vertex_diffuse, alt_lighting,
|
||
|
light_dirs, light_colours, ambient):
|
||
|
if self.failed:
|
||
|
return
|
||
|
config = self.config
|
||
|
config.lighting = lighting
|
||
|
config.vertex_diffuse = vertex_diffuse
|
||
|
config.alt_lighting = alt_lighting
|
||
|
config.light_dirs = mkvec4arr3(light_dirs)
|
||
|
config.light_colours = mkvec4arr3(light_colours)
|
||
|
config.ambient = D3DXVECTOR4(*ambient)
|
||
|
if mymcsup.set_config(config) == -1:
|
||
|
self.failed = True
|
||
|
|
||
|
def set_lighting(self, id):
|
||
|
self.lighting_id = id
|
||
|
self._set_lighting(**self.light_options[id])
|
||
|
|
||
|
def set_animate(self, animate):
|
||
|
if self.failed:
|
||
|
return
|
||
|
self.config.animate = animate
|
||
|
if mymcsup.set_config(self.config) == -1:
|
||
|
self.failed = True
|
||
|
|
||
|
def _set_camera(self, camera):
|
||
|
if self.failed:
|
||
|
return
|
||
|
self.config.camera = mymcsup.D3DXVECTOR3(*camera)
|
||
|
if mymcsup.set_config(self.config) == -1:
|
||
|
self.failed = True
|
||
|
|
||
|
def set_camera(self, id):
|
||
|
self.camera_id = id
|
||
|
self._set_camera(self.camera_options[id])
|
||
|
|
||
|
def evt_context_menu(self, event):
|
||
|
self.update_menu(self.menu)
|
||
|
self.PopupMenu(self.menu)
|
||
|
|
||
|
def evt_menu_animate(self, event):
|
||
|
self.set_animate(not self.config.animate)
|
||
|
|
||
|
def evt_menu_light(self, event):
|
||
|
self.set_lighting(event.GetId())
|
||
|
|
||
|
def evt_menu_camera(self, event):
|
||
|
self.set_camera(event.GetId())
|
||
|
|
||
|
class gui_config(wx.Config):
|
||
|
"""A class for holding the persistant configuration state."""
|
||
|
|
||
|
memcard_dir = "Memory Card Directory"
|
||
|
savefile_dir = "Save File Directory"
|
||
|
ascii = "ASCII Descriptions"
|
||
|
|
||
|
def __init__(self):
|
||
|
wx.Config.__init__(self, "mymc", "Ross Ridge",
|
||
|
style = wx.CONFIG_USE_LOCAL_FILE)
|
||
|
|
||
|
def get_memcard_dir(self, default = None):
|
||
|
return self.Read(gui_config.memcard_dir, default)
|
||
|
|
||
|
def set_memcard_dir(self, value):
|
||
|
return self.Write(gui_config.memcard_dir, value)
|
||
|
|
||
|
def get_savefile_dir(self, default = None):
|
||
|
return self.Read(gui_config.savefile_dir, default)
|
||
|
|
||
|
def set_savefile_dir(self, value):
|
||
|
return self.Write(gui_config.savefile_dir, value)
|
||
|
|
||
|
def get_ascii(self, default = False):
|
||
|
return bool(self.ReadInt(gui_config.ascii, int(bool(default))))
|
||
|
|
||
|
def set_ascii(self, value):
|
||
|
return self.WriteInt(gui_config.ascii, int(bool(value)))
|
||
|
|
||
|
def add_tool(toolbar, id, label, ico):
|
||
|
tbsize = toolbar.GetToolBitmapSize()
|
||
|
bmp = get_icon_resource_bmp(ico, tbsize)
|
||
|
return toolbar.AddLabelTool(id, label, bmp, shortHelp = label)
|
||
|
|
||
|
class gui_frame(wx.Frame):
|
||
|
"""The main top level window."""
|
||
|
|
||
|
ID_CMD_EXIT = wx.ID_EXIT
|
||
|
ID_CMD_OPEN = wx.ID_OPEN
|
||
|
ID_CMD_EXPORT = 103
|
||
|
ID_CMD_IMPORT = 104
|
||
|
ID_CMD_DELETE = wx.ID_DELETE
|
||
|
ID_CMD_ASCII = 106
|
||
|
|
||
|
def message_box(self, message, caption = "mymc", style = wx.OK,
|
||
|
x = -1, y = -1):
|
||
|
return wx.MessageBox(message, caption, style, self, x, y)
|
||
|
|
||
|
def error_box(self, msg):
|
||
|
return self.message_box(msg, "Error", wx.OK | wx.ICON_ERROR)
|
||
|
|
||
|
def mc_error(self, value, filename = None):
|
||
|
"""Display a message box for EnvironmentError exeception."""
|
||
|
|
||
|
if filename == None:
|
||
|
filename = self.mcname
|
||
|
if filename == None:
|
||
|
filename = "???"
|
||
|
filename = getattr(value, "filename", filename)
|
||
|
strerror = getattr(value, "strerror", "unknown error")
|
||
|
return self.error_box(filename + ": " + strerror)
|
||
|
|
||
|
def __init__(self, parent, title, mcname = None):
|
||
|
self.f = None
|
||
|
self.mc = None
|
||
|
self.mcname = None
|
||
|
self.icon_win = None
|
||
|
|
||
|
size = (750, 350)
|
||
|
if mymcsup == None:
|
||
|
size = (500, 350)
|
||
|
wx.Frame.__init__(self, parent, wx.ID_ANY, title, size = size)
|
||
|
|
||
|
wx.EVT_CLOSE(self, self.evt_close)
|
||
|
|
||
|
self.config = gui_config()
|
||
|
self.title = title
|
||
|
|
||
|
self.SetIcons(get_icon_resource("mc4.ico"))
|
||
|
|
||
|
wx.EVT_MENU(self, self.ID_CMD_EXIT, self.evt_cmd_exit)
|
||
|
wx.EVT_MENU(self, self.ID_CMD_OPEN, self.evt_cmd_open)
|
||
|
wx.EVT_MENU(self, self.ID_CMD_EXPORT, self.evt_cmd_export)
|
||
|
wx.EVT_MENU(self, self.ID_CMD_IMPORT, self.evt_cmd_import)
|
||
|
wx.EVT_MENU(self, self.ID_CMD_DELETE, self.evt_cmd_delete)
|
||
|
wx.EVT_MENU(self, self.ID_CMD_ASCII, self.evt_cmd_ascii)
|
||
|
|
||
|
filemenu = wx.Menu()
|
||
|
filemenu.Append(self.ID_CMD_OPEN, "&Open...",
|
||
|
"Opens an existing PS2 memory card image.")
|
||
|
filemenu.AppendSeparator()
|
||
|
self.export_menu_item = filemenu.Append(
|
||
|
self.ID_CMD_EXPORT, "&Export...",
|
||
|
"Export a save file from this image.")
|
||
|
self.import_menu_item = filemenu.Append(
|
||
|
self.ID_CMD_IMPORT, "&Import...",
|
||
|
"Import a save file into this image.")
|
||
|
self.delete_menu_item = filemenu.Append(
|
||
|
self.ID_CMD_DELETE, "&Delete")
|
||
|
filemenu.AppendSeparator()
|
||
|
filemenu.Append(self.ID_CMD_EXIT, "E&xit")
|
||
|
|
||
|
optionmenu = wx.Menu()
|
||
|
self.ascii_menu_item = optionmenu.AppendCheckItem(
|
||
|
self.ID_CMD_ASCII, "&ASCII Descriptions",
|
||
|
"Show descriptions in ASCII instead of Shift-JIS")
|
||
|
|
||
|
|
||
|
wx.EVT_MENU_OPEN(self, self.evt_menu_open);
|
||
|
|
||
|
self.CreateToolBar(wx.TB_HORIZONTAL)
|
||
|
self.toolbar = toolbar = self.GetToolBar()
|
||
|
tbsize = (32, 32)
|
||
|
toolbar.SetToolBitmapSize(tbsize)
|
||
|
add_tool(toolbar, self.ID_CMD_OPEN, "Open", "mc2.ico")
|
||
|
toolbar.AddSeparator()
|
||
|
add_tool(toolbar, self.ID_CMD_IMPORT, "Import", "mc5b.ico")
|
||
|
add_tool(toolbar, self.ID_CMD_EXPORT, "Export", "mc6a.ico")
|
||
|
toolbar.Realize()
|
||
|
|
||
|
self.statusbar = self.CreateStatusBar(2,
|
||
|
style = wx.ST_SIZEGRIP)
|
||
|
self.statusbar.SetStatusWidths([-2, -1])
|
||
|
|
||
|
panel = wx.Panel(self, wx.ID_ANY, (0, 0))
|
||
|
|
||
|
self.dirlist = dirlist_control(panel,
|
||
|
self.evt_dirlist_item_focused,
|
||
|
self.evt_dirlist_select,
|
||
|
self.config)
|
||
|
if mcname != None:
|
||
|
self.open_mc(mcname)
|
||
|
else:
|
||
|
self.refresh()
|
||
|
|
||
|
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||
|
sizer.Add(self.dirlist, 2, wx.EXPAND)
|
||
|
sizer.AddSpacer(5)
|
||
|
|
||
|
icon_win = None
|
||
|
if mymcsup != None:
|
||
|
icon_win = icon_window(panel, self)
|
||
|
if icon_win.failed:
|
||
|
icon_win.Destroy()
|
||
|
icon_win = None
|
||
|
self.icon_win = icon_win
|
||
|
|
||
|
if icon_win == None:
|
||
|
self.info1 = None
|
||
|
self.info2 = None
|
||
|
else:
|
||
|
self.icon_menu = icon_menu = wx.Menu()
|
||
|
icon_win.append_menu_options(self, icon_menu)
|
||
|
optionmenu.AppendSubMenu(icon_menu, "Icon Window")
|
||
|
title_style = wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE
|
||
|
|
||
|
self.info1 = wx.StaticText(panel, -1, "",
|
||
|
style = title_style)
|
||
|
self.info2 = wx.StaticText(panel, -1, "",
|
||
|
style = title_style)
|
||
|
# self.info3 = wx.StaticText(panel, -1, "")
|
||
|
|
||
|
info_sizer = wx.BoxSizer(wx.VERTICAL)
|
||
|
info_sizer.Add(self.info1, 0, wx.EXPAND)
|
||
|
info_sizer.Add(self.info2, 0, wx.EXPAND)
|
||
|
# info_sizer.Add(self.info3, 0, wx.EXPAND)
|
||
|
info_sizer.AddSpacer(5)
|
||
|
info_sizer.Add(icon_win, 1, wx.EXPAND)
|
||
|
|
||
|
sizer.Add(info_sizer, 1, wx.EXPAND | wx.ALL,
|
||
|
border = 5)
|
||
|
|
||
|
menubar = wx.MenuBar()
|
||
|
menubar.Append(filemenu, "&File")
|
||
|
menubar.Append(optionmenu, "&Options")
|
||
|
self.SetMenuBar(menubar)
|
||
|
|
||
|
|
||
|
panel.SetSizer(sizer)
|
||
|
panel.SetAutoLayout(True)
|
||
|
sizer.Fit(panel)
|
||
|
|
||
|
self.Show(True)
|
||
|
|
||
|
if self.mc == None:
|
||
|
self.evt_cmd_open()
|
||
|
|
||
|
def _close_mc(self):
|
||
|
if self.mc != None:
|
||
|
try:
|
||
|
self.mc.close()
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value)
|
||
|
self.mc = None
|
||
|
if self.f != None:
|
||
|
try:
|
||
|
self.f.close()
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value)
|
||
|
self.f = None
|
||
|
self.mcname = None
|
||
|
|
||
|
def refresh(self):
|
||
|
try:
|
||
|
self.dirlist.update(self.mc)
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value)
|
||
|
self._close_mc()
|
||
|
self.dirlist.update(None)
|
||
|
|
||
|
mc = self.mc
|
||
|
|
||
|
self.toolbar.EnableTool(self.ID_CMD_IMPORT, mc != None)
|
||
|
self.toolbar.EnableTool(self.ID_CMD_EXPORT, False)
|
||
|
|
||
|
if mc == None:
|
||
|
status = "No memory card image"
|
||
|
else:
|
||
|
free = mc.get_free_space() / 1024
|
||
|
limit = mc.get_allocatable_space() / 1024
|
||
|
status = "%dK of %dK free" % (free, limit)
|
||
|
self.statusbar.SetStatusText(status, 1)
|
||
|
|
||
|
def open_mc(self, filename):
|
||
|
self._close_mc()
|
||
|
self.statusbar.SetStatusText("", 1)
|
||
|
if self.icon_win != None:
|
||
|
self.icon_win.load_icon(None, None)
|
||
|
|
||
|
f = None
|
||
|
try:
|
||
|
f = file(filename, "r+b")
|
||
|
mc = ps2mc.ps2mc(f)
|
||
|
except EnvironmentError, value:
|
||
|
if f != None:
|
||
|
f.close()
|
||
|
self.mc_error(value, filename)
|
||
|
self.SetTitle(self.title)
|
||
|
self.refresh()
|
||
|
return
|
||
|
|
||
|
self.f = f
|
||
|
self.mc = mc
|
||
|
self.mcname = filename
|
||
|
self.SetTitle(filename + " - " + self.title)
|
||
|
self.refresh()
|
||
|
|
||
|
def evt_menu_open(self, event):
|
||
|
self.import_menu_item.Enable(self.mc != None)
|
||
|
selected = self.mc != None and len(self.dirlist.selected) > 0
|
||
|
self.export_menu_item.Enable(selected)
|
||
|
self.delete_menu_item.Enable(selected)
|
||
|
self.ascii_menu_item.Check(self.config.get_ascii())
|
||
|
if self.icon_win != None:
|
||
|
self.icon_win.update_menu(self.icon_menu)
|
||
|
|
||
|
def evt_dirlist_item_focused(self, event):
|
||
|
if self.icon_win == None:
|
||
|
return
|
||
|
|
||
|
mc = self.mc
|
||
|
|
||
|
i = event.GetData()
|
||
|
(ent, icon_sys, size, title) = self.dirlist.dirtable[i]
|
||
|
self.info1.SetLabel(title[0])
|
||
|
self.info2.SetLabel(title[1])
|
||
|
|
||
|
a = ps2save.unpack_icon_sys(icon_sys)
|
||
|
try:
|
||
|
mc.chdir("/" + ent[8])
|
||
|
f = mc.open(a[15], "rb")
|
||
|
try:
|
||
|
icon = f.read()
|
||
|
finally:
|
||
|
f.close()
|
||
|
except EnvironmentError, value:
|
||
|
print "icon failed to load", value
|
||
|
self.icon_win.load_icon(None, None)
|
||
|
return
|
||
|
|
||
|
self.icon_win.load_icon(icon_sys, icon)
|
||
|
|
||
|
def evt_dirlist_select(self, event):
|
||
|
self.toolbar.EnableTool(self.ID_CMD_IMPORT, self.mc != None)
|
||
|
self.toolbar.EnableTool(self.ID_CMD_EXPORT,
|
||
|
len(self.dirlist.selected) > 0)
|
||
|
|
||
|
def evt_cmd_open(self, event = None):
|
||
|
fn = wx.FileSelector("Open Memory Card Image",
|
||
|
self.config.get_memcard_dir(""),
|
||
|
"Mcd001.ps2", "ps2", "*.ps2",
|
||
|
wx.FD_FILE_MUST_EXIST | wx.FD_OPEN,
|
||
|
self)
|
||
|
if fn == "":
|
||
|
return
|
||
|
self.open_mc(fn)
|
||
|
if self.mc != None:
|
||
|
dirname = os.path.dirname(fn)
|
||
|
if os.path.isabs(dirname):
|
||
|
self.config.set_memcard_dir(dirname)
|
||
|
|
||
|
def evt_cmd_export(self, event):
|
||
|
mc = self.mc
|
||
|
if mc == None:
|
||
|
return
|
||
|
|
||
|
selected = self.dirlist.selected
|
||
|
dirtable = self.dirlist.dirtable
|
||
|
sfiles = []
|
||
|
for i in selected:
|
||
|
dirname = dirtable[i][0][8]
|
||
|
try:
|
||
|
sf = mc.export_save_file("/" + dirname)
|
||
|
longname = ps2save.make_longname(dirname, sf)
|
||
|
sfiles.append((dirname, sf, longname))
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value. dirname)
|
||
|
|
||
|
if len(sfiles) == 0:
|
||
|
return
|
||
|
|
||
|
dir = self.config.get_savefile_dir("")
|
||
|
if len(selected) == 1:
|
||
|
(dirname, sf, longname) = sfiles[0]
|
||
|
fn = wx.FileSelector("Export " + dirname,
|
||
|
dir, longname, "psu",
|
||
|
"EMS save file (.psu)|*.psu"
|
||
|
"|MAXDrive save file (.max)"
|
||
|
"|*.max",
|
||
|
(wx.FD_OVERWRITE_PROMPT
|
||
|
| wx.FD_SAVE),
|
||
|
self)
|
||
|
if fn == "":
|
||
|
return
|
||
|
try:
|
||
|
f = file(fn, "wb")
|
||
|
try:
|
||
|
if fn.endswith(".max"):
|
||
|
sf.save_max_drive(f)
|
||
|
else:
|
||
|
sf.save_ems(f)
|
||
|
finally:
|
||
|
f.close()
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value, fn)
|
||
|
return
|
||
|
|
||
|
dir = os.path.dirname(fn)
|
||
|
if os.path.isabs(dir):
|
||
|
self.config.set_savefile_dir(dir)
|
||
|
|
||
|
self.message_box("Exported " + fn + " successfully.")
|
||
|
return
|
||
|
|
||
|
dir = wx.DirSelector("Export Save Files", dir, parent = self)
|
||
|
if dir == "":
|
||
|
return
|
||
|
count = 0
|
||
|
for (dirname, sf, longname) in sfiles:
|
||
|
fn = os.path.join(dir, longname) + ".psu"
|
||
|
try:
|
||
|
f = file(fn, "wb")
|
||
|
sf.save_ems(f)
|
||
|
f.close()
|
||
|
count += 1
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value, fn)
|
||
|
if count > 0:
|
||
|
if os.path.isabs(dir):
|
||
|
self.config.set_savefile_dir(dir)
|
||
|
self.message_box("Exported %d file(s) successfully."
|
||
|
% count)
|
||
|
|
||
|
|
||
|
def _do_import(self, fn):
|
||
|
sf = ps2save.ps2_save_file()
|
||
|
f = file(fn, "rb")
|
||
|
try:
|
||
|
ft = ps2save.detect_file_type(f)
|
||
|
f.seek(0)
|
||
|
if ft == "max":
|
||
|
sf.load_max_drive(f)
|
||
|
elif ft == "psu":
|
||
|
sf.load_ems(f)
|
||
|
elif ft == "cbs":
|
||
|
sf.load_codebreaker(f)
|
||
|
elif ft == "sps":
|
||
|
sf.load_sharkport(f)
|
||
|
elif ft == "npo":
|
||
|
self.error_box(fn + ": nPort saves"
|
||
|
" are not supported.")
|
||
|
return
|
||
|
else:
|
||
|
self.error_box(fn + ": Save file format not"
|
||
|
" recognized.")
|
||
|
return
|
||
|
finally:
|
||
|
f.close()
|
||
|
|
||
|
if not self.mc.import_save_file(sf, True):
|
||
|
self.error_box(fn + ": Save file already present.")
|
||
|
|
||
|
def evt_cmd_import(self, event):
|
||
|
if self.mc == None:
|
||
|
return
|
||
|
|
||
|
dir = self.config.get_savefile_dir("")
|
||
|
fd = wx.FileDialog(self, "Import Save File", dir,
|
||
|
wildcard = ("PS2 save files"
|
||
|
" (.cbs;.psu;.max;.sps;.xps)"
|
||
|
"|*.cbs;*.psu;*.max;*.sps;*.xps"
|
||
|
"|All files|*.*"),
|
||
|
style = (wx.FD_OPEN | wx.FD_MULTIPLE
|
||
|
| wx.FD_FILE_MUST_EXIST))
|
||
|
if fd == None:
|
||
|
return
|
||
|
r = fd.ShowModal()
|
||
|
if r == wx.ID_CANCEL:
|
||
|
return
|
||
|
|
||
|
success = None
|
||
|
for fn in fd.GetPaths():
|
||
|
try:
|
||
|
self._do_import(fn)
|
||
|
success = fn
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value, fn)
|
||
|
|
||
|
if success != None:
|
||
|
dir = os.path.dirname(success)
|
||
|
if os.path.isabs(dir):
|
||
|
self.config.set_savefile_dir(dir)
|
||
|
self.refresh()
|
||
|
|
||
|
def evt_cmd_delete(self, event):
|
||
|
mc = self.mc
|
||
|
if mc == None:
|
||
|
return
|
||
|
|
||
|
selected = self.dirlist.selected
|
||
|
dirtable = self.dirlist.dirtable
|
||
|
|
||
|
dirnames = [dirtable[i][0][8]
|
||
|
for i in selected]
|
||
|
if len(selected) == 1:
|
||
|
title = dirtable[list(selected)[0]][3]
|
||
|
s = dirnames[0] + " (" + single_title(title) + ")"
|
||
|
else:
|
||
|
s = ", ".join(dirnames)
|
||
|
if len(s) > 200:
|
||
|
s = s[:200] + "..."
|
||
|
r = self.message_box("Are you sure you want to delete "
|
||
|
+ s + "?",
|
||
|
"Delete Save File Confirmation",
|
||
|
wx.YES_NO)
|
||
|
if r != wx.YES:
|
||
|
return
|
||
|
|
||
|
for dn in dirnames:
|
||
|
try:
|
||
|
mc.rmdir("/" + dn)
|
||
|
except EnvironmentError, value:
|
||
|
self.mc_error(value, dn)
|
||
|
|
||
|
mc.check()
|
||
|
self.refresh()
|
||
|
|
||
|
def evt_cmd_ascii(self, event):
|
||
|
self.config.set_ascii(not self.config.get_ascii())
|
||
|
self.refresh()
|
||
|
|
||
|
def evt_cmd_exit(self, event):
|
||
|
self.Close(True)
|
||
|
|
||
|
def evt_close(self, event):
|
||
|
self._close_mc()
|
||
|
self.Destroy()
|
||
|
|
||
|
def run(filename = None):
|
||
|
"""Display a GUI for working with memory card images."""
|
||
|
|
||
|
wx_app = wx.PySimpleApp()
|
||
|
frame = gui_frame(None, "mymc", filename)
|
||
|
return wx_app.MainLoop()
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
import gc
|
||
|
gc.set_debug(gc.DEBUG_LEAK)
|
||
|
|
||
|
run("test.ps2")
|
||
|
|
||
|
gc.collect()
|
||
|
for o in gc.garbage:
|
||
|
print
|
||
|
print o
|
||
|
if type(o) == ps2mc.ps2mc_file:
|
||
|
for m in dir(o):
|
||
|
print m, getattr(o, m)
|
||
|
|
||
|
|
||
|
# while True:
|
||
|
# for o in gc.garbage:
|
||
|
# if type(o) == ps2mc.ps2mc_file:
|
||
|
# for m in dir(o):
|
||
|
# if getattr(o, m) == None:
|
||
|
# continue
|
||
|
# if (m == "__del__"
|
||
|
# or m == "__class__"
|
||
|
# or m == "__dict__"
|
||
|
# or m == "__weakref__"):
|
||
|
# continue
|
||
|
# print m
|
||
|
# setattr(o, m, None)
|
||
|
# o = None
|
||
|
# break
|
||
|
# break
|
||
|
# del gc.garbage[:]
|
||
|
# gc.collect()
|