Une interface graphique pour l'émulateur de console de jeux SEGA Megadrive DGen
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

875 lines
32 KiB

#!/usr/bin/python
# -*- coding: utf8-*-
import os
import subprocess
from Tkinter import *
import Image
import ImageTk
import tkMessageBox
import tkFileDialog
from zipfile import *
import glob
import base64
import cStringIO
########################################################################
# Library
########################################################################
# The About text
ABOUT = """
pyDGen v0.1
pyDGen is a python/TkInter graphical interface to the DGen SEGA GENESIS emulator.
Released over the WTFPL licence 2.0 by
Doug Le Tough (doug.letough@free.fr).
http://dgen.sourceforge.net/
"""
# The licence text
LICENCE = """
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO."""
def get_dgen_executable():
"""
Try to autodetect the DGen executable
by looking into the PATH system environment variable.
If not found return an explicit string.
"""
sys_path = os.environ['PATH']
path_dirs = sys_path.split(':')
for path_dir in path_dirs :
dgen = os.path.join(path_dir, 'dgen')
if os.path.isfile(dgen) :
return dgen
tkMessageBox.showwarning("pyDgen - NO DGen found", "No DGen executable found is system path.\n\nPlease install DGen !")
def get_home_dir():
"""
Return path to user home directory
"""
return os.path.expanduser("~")
def get_rom_path():
"""
Check the default DGen ROM path and return it.
If a custom ROM path is defined in the config window
it will returned instead. If none of above conditions are found,
return "Not defined".
The first launch of DGen will automatically create the default
ROM path dir. See man 1 dgen.
"""
standard_rom_path = os.path.join(get_home_dir(), '.dgen/roms/')
if not os.path.isdir(standard_rom_path):
standard_rom_path = "Not defined"
return standard_rom_path
def get_screenshot_path():
"""
Return the default DGen screenshot path.
The first screenshot made from DGen (F12 during play)
will automatically create the default screenshots path.
See man 1 dgen.
"""
return os.path.join(get_home_dir(), '.dgen/screenshots/')
def refresh_rom_listbox(window):
"""
Refresh the main window ROM listbox according to
ROM_PATH (Default to ~/.dgen/roms/ .See configuration.)
"""
window.delete_rom_button['state'] = 'disabled'
window.delete_screenshot_button['state'] = 'disabled'
window.play_button['state'] = 'disabled'
window.display_screenshot("")
window.rom_listbox.delete(0, END)
window.status_label_var.set('Please wait while processing...')
window.update()
# Sort by game name rather rom file name
window.rom_list = sorted(get_rom_list(window.DGEN_ROM_PATH.get()), key=lambda tup: tup[1].lower())
total = len(window.rom_list)
single = total/100.0
item = 0
for rom in window.rom_list :
game_name = rom[1]
window.rom_listbox.insert(END, game_name)
if game_name[0:15] == '* NO NAME FOUND':
window.rom_listbox.itemconfig(item, fg='red')
window.status_label_var.set(rom[0])
window.progress_label_var.set(str(round(item/single)).split('.')[0]+'%')
window.update()
item +=1
window.status_label_var.set(str(total)+' ROM found in '+window.DGEN_ROM_PATH.get())
window.progress_label_var.set('')
def get_rom_list(rom_path):
"""
Return the whole ROM list according to configuration.
All zip (*.zip), bin (*.bin) and gen (*.gen) files in the
ROM_PATH are considered valid rom files.
"""
roms = []
if os.path.isdir(rom_path):
bin_rom = glob.glob(os.path.join(rom_path,"*.bin"))
bin_rom += glob.glob(os.path.join(rom_path,"*.gen"))
bin_rom += glob.glob(os.path.join(rom_path,"*.zip"))
for rom in bin_rom :
if os.path.isfile(rom):
roms.append((rom, get_rom_name(rom)))
else:
tkMessageBox.showerror("pyDgen - Error", "ROM path is not valid !")
return roms
def get_rom_name(rom_file):
"""
Try to retrieve rom name from within the rom itself.
Unzip it if necessary to do so.
Note that some ROM do not respect ROM specification or do not
provide any name or an non ascii name.
In the latter case, non ascii characters are dropped.
ROM specification can be found here :
http://www.zophar.net/fileuploads/2/10614uauyw/Genesis_ROM_Format.txt
"""
data = ""
if is_zipfile(rom_file):
# we need to retrieve the .bin or the .gen rom file
# in the zip archive
zip_archive = ZipFile(rom_file)
for zip_content in zip_archive.namelist():
decomposed = zip_content.split('.')
extension = decomposed[len(decomposed)-1]
if extension == 'bin' or extension == 'gen':
gen = zip_archive.read(zip_content)
# Get the international game name
data = gen[336:384]
if len(data.strip()) > 0 :
data = data.strip()
else :
# No international game name, let's seek for
# local game name
data = gen[288:336]
data = data.strip()
else:
# Let's guess its a binary .gen or .bin rom file
with open(rom_file, "rb") as gen:
gen.seek(336)
# Get the international game name
data = bytearray(gen.read(48))
if len(data.strip()) > 0 :
data = data.strip()
else :
# No international game name, let's seek for
# local game name
gen.seek(288)
data = bytearray(gen.read(48))
data = data.strip()
# remove all non-printable (and some non-ascii) characters
# authorized ascii codes :
authorized_chars = [32, 33, 34, 39, 44]
for i in range(48, 59):
authorized_chars.append(i)
authorized_chars.append(63)
for i in range(65, 91):
authorized_chars.append(i)
for i in range(97, 123):
authorized_chars.append(i)
data = (c for c in str(data) if ord(c) in authorized_chars)
data = ''.join(data)
if len(data) == 0 :
data = "* NO NAME FOUND OR NAME NOT PRINTABLE in "+rom_file+" *"
return data
########################################################################
# Main window
########################################################################
class PyDGenGui(Frame):
"""
main_frame
|___menu_frame
| |__prefs_button
| |__licence_button
| |__about_button
| |__blank_label
|
|__middle_frame
| |___left_frame
| | |__rom_list_frame
| | |__scrollbar
| | |__rom_listbox
| |
| |__right_frame
| |__screenshot_frame
| | |__screenshot_canvas
| |__play_button
| |__delete_rom_buton
| |__delete_screenshot_button
|
|__status_label
HOME = User home dir (autodetected)
DGEN_ROM_PATH = ROMs folder (default to ~/.dgen/roms/,can be overridden)
DGEN_SCREENSHOT_PATH = Sreenshots folder (default to ~/.dgen/screenshots/, can be overridden)
DGEN_RC = DGen rc file (default to ~/.dgen/dgenrc, can be overridden)
DGEN_EXEC = DGen executable (autodetected at start, can be overridden)
VIDEO_MODE = PAL or NTSC (default to PAL, can be overridden)
VIDEO_OPENGL = OpenGL setting (default to 1, true, can be overriden)
VIDEO_FULLSCREEN = Fullscreen mode (default to 1, true, can be overriden)
VIDEO_WIDTH = Width (default to auto, can be overridden)
VIDEO_HEIGHT = Height (default to auto, can be overridden)
VIDEO_XFACTOR = Width factor (default to none, can be overridden)
VIDEO_YFACTOR = Height factor (default to none, can be overridden)
REGION = Region (default to auto, can be overridden)
FRAMERATE = Framerate (default to auto, can be overridden)
SLEEP = Sleeping time in seconds after each frame (default to 0, can be overridden)
MINGW = Prevent terminal detaching (MingW environment only, default to 0, can be overridden)
"""
def __init__(self, main_window, master=None, **kwargs):
"""
Initialize main window and add the main frame
Initialize all configuration variables
"""
self.HOME = StringVar()
self.HOME.set(get_home_dir())
self.DGEN_ROM_PATH = StringVar()
self.DGEN_ROM_PATH.set(get_rom_path())
self.DGEN_SCREENSHOT_PATH = StringVar()
self.DGEN_SCREENSHOT_PATH.set(get_screenshot_path())
self.DGEN_RC = StringVar()
self.DGEN_EXEC = StringVar()
self.DGEN_EXEC.set(get_dgen_executable())
self.VIDEO_MODE = StringVar()
self.VIDEO_MODE.set("PAL")
self.VIDEO_OPENGL = IntVar()
self.VIDEO_OPENGL.set(1)
self.VIDEO_FULLSCREEN = IntVar()
self.VIDEO_FULLSCREEN.set(1)
self.VIDEO_WIDTH = IntVar()
self.VIDEO_WIDTH.set(-1)
self.VIDEO_HEIGHT = IntVar()
self.VIDEO_HEIGHT.set(-1)
self.VIDEO_XFACTOR = IntVar()
self.VIDEO_XFACTOR.set(-1)
self.VIDEO_YFACTOR = IntVar()
self.VIDEO_YFACTOR.set(-1)
self.REGION = StringVar()
self.REGION.set("AUTO")
self.FRAMERATE = IntVar()
self.FRAMERATE.set(60)
self.SLEEP = IntVar()
self.SLEEP.set(0)
self.MINGW = IntVar()
self.MINGW.set(0)
self.rom_list = [] # List of available rom
self.img = None # Reference to screenshot image
self.status_label_var = StringVar() # Status bar label
self.status_label_var.set("Please wait while processing...")
self.progress_label_var = StringVar() # Progress bar label
self.main_window = main_window
Frame.__init__(self, main_window, **kwargs)
self.add_widgets()
self.pack(fill=BOTH, expand=1)
self.rom_listbox.focus_set()
def add_widgets(self):
"""
Add all subframes containers and widgets to main frame
"""
self.add_menu_frame(self)
self.add_middle_frame(self)
self.status_label = Label(self, textvariable=self.status_label_var, relief=SUNKEN, borderwidth=1, anchor=W)
self.progress_label = Label(self, textvariable=self.progress_label_var, relief=SUNKEN, borderwidth=1, width=10)
self.progress_label.pack(fill=X, side=LEFT, padx=5, pady=2)
self.status_label.pack(fill=X, side=RIGHT, padx=5, pady=2, expand=1)
def add_menu_frame(self, main_frame):
"""
Add the menu bar to the main frame
"""
menu_frame = Frame(main_frame, relief=SUNKEN, borderwidth=1)
config_button = Button(menu_frame, text="Configure DGen", command=self.display_config_window)
about_button = Button(menu_frame, text="About", command=self.display_about)
licence_button = Button(menu_frame, text="Licence", command=self.display_licence)
spacer_button = Button(menu_frame, state=DISABLED)
config_button.pack(side=LEFT)
about_button.pack(side=LEFT)
licence_button.pack(side=LEFT)
menu_frame.pack(fill=X)
spacer_button.pack(fill=X, side=RIGHT, expand=1)
def add_middle_frame(self, main_frame):
"""
Add the middle frame container to main frame
"""
middle_frame = Frame(main_frame, borderwidth=1)
self.add_left_frame(middle_frame)
self.add_right_frame(middle_frame)
middle_frame.pack(fill=BOTH, expand=1)
def add_left_frame(self, middle_frame):
"""
Add the left frame container and its components widgets
(ROMs listbox and scrollbar) to the middle frame container
"""
left_frame = Frame(middle_frame, borderwidth=1)
rom_list_frame = LabelFrame(left_frame, text='Choose a ROM')
scrollbar = Scrollbar(rom_list_frame, orient=VERTICAL)
# Keep the self prefix ! rom_listbox has to be accessible
# from outside to be refreshed
self.rom_listbox = Listbox(rom_list_frame,
width=50,
relief=SUNKEN,
borderwidth=1,
selectmode = SINGLE,
yscrollcommand=scrollbar.set)
scrollbar.config(command=self.rom_listbox.yview)
self.rom_listbox.bind('<<ListboxSelect>>', self.rom_select)
self.rom_listbox.bind('<Return>', self.keyboard_play)
self.rom_listbox.bind('<Delete>', self.keyboard_delete)
self.rom_listbox.bind('<Escape>', self.keyboard_quit)
self.rom_listbox.bind('<Up>', self.keyboard_up)
self.rom_listbox.bind('<Down>', self.keyboard_down)
self.rom_listbox.bind('<Key>', self.keyboard_key)
rom_list_frame.pack(fill=BOTH, expand=1, padx=5, pady=5)
scrollbar.pack(side=RIGHT, fill=Y, padx=5, pady=5)
self.rom_listbox.pack(side=LEFT, fill=BOTH, expand=1, padx=5, pady=5)
left_frame.pack(side=LEFT, fill=BOTH, expand=1)
def add_right_frame(self, middle_frame):
"""
Add the right frame container and its components wigdgets
(screenshot canvas and buttons) to the middle frame container
"""
right_frame = Frame(middle_frame, borderwidth=1)
screenshot_frame = LabelFrame(right_frame, text='Screenshot')
# Keep the self prefix ! screenshot_canvas has to be accessible
# from outside to be refreshed
self.screenshot_canvas = Canvas(screenshot_frame, relief=SUNKEN, borderwidth=1)
self.screenshot_canvas.bind("<Configure>", self.resize_screenshot)
self.screenshot_canvas.pack(fill=BOTH, expand=1, padx=5, pady=5)
screenshot_frame.pack(fill=BOTH, expand=1, padx=5, pady=5)
# Keep the self prefix ! delete_screenshot_button, delete_rom_button
# and play_button have to be accessible from outside to be disabled
self.play_button = Button(right_frame, text="Play", state=DISABLED, command=self.play)
self.delete_rom_button = Button(right_frame, text="Delete ROM", state=DISABLED, command=self.delete_rom)
self.delete_screenshot_button = Button(right_frame, text="Delete screenshot", state=DISABLED, command=self.delete_screenshot)
refresh_list_button = Button(right_frame, text="Refresh ROM list", command=self.refresh_list)
self.play_button.pack(side=RIGHT, fill=X, expand=1, padx=5, pady=5)
refresh_list_button.pack(side=RIGHT, fill=X, expand=1, padx=5, pady=5)
self.delete_rom_button.pack(side=LEFT, fill=X, expand=1, padx=5, pady=5)
self.delete_screenshot_button.pack(side=LEFT, fill=X, expand=1, padx=5, pady=5)
right_frame.pack(side=RIGHT, fill=BOTH, expand=1)
def play(self):
"""
Launch DGen with all configuration parameters
"""
try :
rom = self.rom_list[int(self.rom_listbox.curselection()[0])][0]
dgen_options = self.parse_conf()
dgen_options.append(rom)
subprocess.call(dgen_options)
except IndexError :
tkMessageBox.showwarning("pyDgen - No ROM selected", "Please, choose a ROM to play !")
except Exception as e :
tkMessageBox.showerror("pyDgen - Error "+str(e.args[0]), str(e.args[1]))
def parse_conf(self):
"""
Parse all configuration variable and return the appropriate
command line.
"""
dgen_options = []
dgen_options.append(self.DGEN_EXEC.get())
if len(self.DGEN_RC.get()) :
dgen_options.append('-r')
dgen_options.append(self.DGEN_RC.get())
if self.VIDEO_FULLSCREEN.get():
dgen_options.append('-f')
dgen_options.append('-g')
dgen_options.append(str(self.VIDEO_OPENGL.get()))
if self.VIDEO_MODE.get() == 'PAL' :
dgen_options.append('-P')
dgen_options.append('-G')
dgen_options.append(str(self.VIDEO_WIDTH.get())+'x'+str(self.VIDEO_HEIGHT.get()))
dgen_options.append('-X')
dgen_options.append(str(self.VIDEO_XFACTOR.get()))
dgen_options.append('-Y')
dgen_options.append(str(self.VIDEO_YFACTOR.get()))
if self.REGION.get()[0] != 'A':
dgen_options.append("-R")
dgen_options.append(self.REGION.get()[0])
dgen_options.append('-H '+str(self.FRAMERATE.get()))
if self.MINGW.get() :
dgen_options.append('-m')
if self.SLEEP.get() :
dgen_options.append('-n')
dgen_options.append(str(self.SLEEP.get()))
dgen_options.append('-s 0')
return dgen_options
def delete_rom(self):
"""
Delete the selected ROM
"""
try :
rom = self.rom_list[int(self.rom_listbox.curselection()[0])][0]
rom_name = os.path.basename(rom)
screenshot = rom_name[0:rom_name.rfind('.')]+"-000000.tga"
screenshot = os.path.join(str(self.DGEN_SCREENSHOT_PATH.get()), screenshot)
answer = tkMessageBox.askquestion("Delete", \
"Do you really want to delete \n"+self.rom_list[int(self.rom_listbox.curselection()[0])][0].split(self.DGEN_ROM_PATH.get())[1]
+"\n ("+self.rom_list[int(self.rom_listbox.curselection()[0])][1]+") ?", \
icon='warning', \
default=tkMessageBox.NO)
if answer == 'yes':
if os.path.isfile(screenshot):
os.remove(screenshot)
self.display_screenshot(screenshot)
if os.path.isfile(rom):
os.remove(rom)
self.rom_list.remove(self.rom_list[int(self.rom_listbox.curselection()[0])])
self.rom_listbox.delete(self.rom_listbox.curselection())
except IndexError :
tkMessageBox.showerror("pyDgen - Error", "Please, choose a ROM to delete !")
except Exception as e:
tkMessageBox.showerror("pyDgen - Error", str(e))
def delete_screenshot(self):
"""
Delete selected screeenshot
"""
try :
rom_name = os.path.basename(self.rom_list[int(self.rom_listbox.curselection()[0])][0])
screenshot = rom_name[0:rom_name.find('.')]+"-000000.tga"
screenshot = os.path.join(str(self.DGEN_SCREENSHOT_PATH.get()), screenshot)
answer = tkMessageBox.askquestion("Delete", \
"Do you really want to delete \n"+screenshot.split(self.DGEN_SCREENSHOT_PATH.get())[1]+" ?", \
icon='warning', \
default=tkMessageBox.NO)
if answer == 'yes':
if os.path.isfile(screenshot):
os.remove(screenshot)
self.display_screenshot(screenshot)
except IndexError :
tkMessageBox.showerror("pyDgen - Error", "Please, choose a ROM to delete the screenshot from !")
def resize_screenshot(self, event):
"""
Resize screenshot according to main window new geometry
"""
try :
rom_name = os.path.basename(self.rom_list[int(self.rom_listbox.curselection()[0])][0])
except IndexError:
# No ROM is selected
rom_name = ""
# Screenshot format is :
# First part of ROM name splitted at the *first* '.' character concatenated
# with "-xxxxxx.tga" where 000000 < xxxxxx < 999999
screenshot = rom_name[0:rom_name.find('.')]+"-000000.tga"
screenshot = os.path.join(str(self.DGEN_SCREENSHOT_PATH.get()), screenshot)
self.display_screenshot(screenshot)
def display_screenshot(self, screenshot):
"""
Display the selected ROM screenshot.
Screenshots are saved in ~/.dgen/screenshot by DGen.
See man 1 dgen.
"""
if os.path.isfile(screenshot):
canvas_width = self.screenshot_canvas.winfo_width()
canvas_height = self.screenshot_canvas.winfo_height()
sc = Image.open(screenshot)
sc = sc.resize((canvas_width,canvas_height), Image.ANTIALIAS)
self.img = ImageTk.PhotoImage(sc)
self.screenshot_canvas.create_image(0, 0, image = self.img, anchor = NW)
self.delete_screenshot_button['state'] = 'normal'
else :
# No screenshot available
self.img = None
self.delete_screenshot_button['state'] = 'disabled'
def rom_select(self, event):
"""
Enable buttons and display screenshot of the selected ROM.
"""
self.play_button['state'] = 'normal'
self.delete_rom_button['state'] = 'normal'
rom_name = os.path.basename(self.rom_list[int(event.widget.curselection()[0])][0])
rom_number = event.widget.curselection()[0]
rom_total = len(self.rom_list)
self.progress_label_var.set(str(rom_number+1)+"/"+str(rom_total))
self.status_label_var.set(rom_name)
# Screenshot format is :
# First part of ROM name splitted at the *first* '.' character concatenated
# with "-xxxxxx.tga" where 000000 < xxxxxx < 999999
screenshot = rom_name[0:rom_name.find('.')]+"-000000.tga"
screenshot = os.path.join(str(self.DGEN_SCREENSHOT_PATH.get()), screenshot)
self.display_screenshot(screenshot)
def keyboard_play(self, event):
"""
Launch DGen (Used when hitting "Enter" or "Return" key).
"""
self.play()
def keyboard_down(self, event):
"""
Select the next game
"""
try:
cur_sel = int(event.widget.curselection()[0])
except IndexError:
cur_sel = 0
if cur_sel + 1 > len(self.rom_list) - 1 :
cur_sel = len(self.rom_list) - 2
self.rom_listbox.select_clear(cur_sel)
self.rom_listbox.select_set(cur_sel + 1)
self.rom_listbox.activate(cur_sel)
self.rom_listbox.see(cur_sel + 1)
self.rom_select(event)
def keyboard_up(self, event):
"""
Select the previous game
"""
try:
cur_sel = int(event.widget.curselection()[0])
except IndexError:
cur_sel = 0
if cur_sel -1 < 0 :
cur_sel = 1
self.rom_listbox.select_clear(cur_sel)
self.rom_listbox.select_set(cur_sel - 1)
self.rom_listbox.activate(cur_sel)
self.rom_listbox.see(cur_sel - 1)
self.rom_select(event)
def keyboard_key(self, event):
"""
Select the next game starting with
the pressed key
"""
rom_pos = 0
try:
cur_sel = int(event.widget.curselection()[0])
except IndexError:
cur_sel = 0
for rom in self.rom_list:
if rom[1][0].lower() == event.char.lower():
if self.rom_list[cur_sel][1][0].lower() == event.char.lower():
if self.rom_list[cur_sel+1][1][0].lower() == event.char.lower():
final_sel = cur_sel+1
else:
final_sel = rom_pos
else:
final_sel = rom_pos
self.rom_listbox.select_clear(cur_sel)
self.rom_listbox.select_set(final_sel)
self.rom_listbox.activate(final_sel)
self.rom_listbox.see(final_sel)
self.rom_select(event)
return
rom_pos += 1
def keyboard_delete(self, event):
"""
Delete selected ROM (Used when hitting "Del." key.
"""
self.delete_rom()
def keyboard_quit(self, event):
"""
Quit pyDGen (Used when hitting "ESC." key.
"""
self.main_window.destroy()
####################################################################
# Other windows
####################################################################
def display_config_window(self):
"""
Configuration window (modal window)
"""
self.config_window = Toplevel(self.main_window)
self.config_window.resizable(0,0)
self.config_window.wm_title('pyDGen - Configuration')
self.config_window.grab_set()
self.config_window.transient(self.main_window)
main_config_frame = self.add_config_main_frame(self.config_window)
self.add_config_widgets(main_config_frame)
Button(main_config_frame, text='Apply', command=self.config_window.destroy).pack(side=TOP, pady=5)
main_config_frame.pack(fill=BOTH, expand=1)
def add_config_main_frame(self, window):
"""
Add main frame to main window
"""
return Frame(window, relief=RAISED, borderwidth=1)
def add_config_widgets(self, master_frame):
"""
Add all widgets to config main frame
"""
self.add_config_paths_frame(master_frame)
self.add_config_video_frame(master_frame)
self.add_config_region_and_perfs_frame(master_frame)
def add_config_paths_frame(self, master_frame):
"""
Add all path section widgets
"""
paths_frame = LabelFrame(master_frame, text='Paths')
# DGen path
dgen_exec_frame = Frame(paths_frame)
dgen_exec_label = Label(dgen_exec_frame, text='DGen executable :')
dgen_exec_entry = Entry(dgen_exec_frame, relief=SUNKEN, borderwidth=1, textvariable=self.DGEN_EXEC)
dgen_exec_button = Button(dgen_exec_frame, text='...', command=self.browse_for_dgen_exec)
dgen_exec_label.pack(side=LEFT, padx=5, pady=2)
dgen_exec_entry.pack(side=LEFT, fill=X, expand=1, padx=5, pady=2)
dgen_exec_button.pack(side=LEFT, padx=5, pady=2)
dgen_exec_frame.pack(fill=X, expand=1, pady=2)
# DGen rc path
dgenrc_frame = Frame(paths_frame)
dgenrc_label = Label(dgenrc_frame, text='Additional DGen config file (override standard dgenrc) :')
dgenrc_entry = Entry(dgenrc_frame, relief=SUNKEN, borderwidth=1, textvariable=self.DGEN_RC)
dgenrc_button = Button(dgenrc_frame, text='...', command=self.browse_for_dgenrc)
dgenrc_label.pack(side=LEFT, padx=5, pady=2)
dgenrc_entry.pack(side=LEFT, fill=X, expand=1, padx=5, pady=2)
dgenrc_button.pack(side=LEFT, padx=5, pady=2)
dgenrc_frame.pack(fill=X, expand=1, pady=2)
# ROMs path
rom_path_frame = Frame(paths_frame)
rom_path_label = Label(rom_path_frame, text='ROMs directory :')
rom_path_entry = Entry(rom_path_frame, relief=SUNKEN, borderwidth=1, textvariable=self.DGEN_ROM_PATH)
rom_path_button = Button(rom_path_frame, text='...', command=self.browse_for_rom_path)
rom_path_label.pack(side=LEFT, padx=5, pady=2)
rom_path_entry.pack(side=LEFT, fill=X, expand=1, padx=5, pady=2)
rom_path_button.pack(side=LEFT, padx=5, pady=2)
rom_path_frame.pack(fill=X, expand=1, pady=2)
paths_frame.pack(fill=X, expand=1, side=TOP, padx=5, pady=5)
def add_config_video_frame(self, master_frame):
"""
Add all video section widgets
"""
video_frame = LabelFrame(master_frame, text='Video')
# PAL/NTSC, OpenGL and fullscreen settings
video_mode_frame = Frame(video_frame)
radio_pal = Radiobutton(video_mode_frame, text='PAL', value='PAL', variable=self.VIDEO_MODE)
radio_ntsc = Radiobutton(video_mode_frame, text='NTSC', value='NTSC', variable=self.VIDEO_MODE)
check_opengl = Checkbutton(video_mode_frame, text='OpenGL (if available)', variable=self.VIDEO_OPENGL, onvalue='1', offvalue='0')
check_fullscreen = Checkbutton(video_mode_frame, text='Fullscreen (if possible)', variable=self.VIDEO_FULLSCREEN, onvalue='1', offvalue='0')
radio_pal.pack(side=LEFT, padx=5, pady=2)
radio_ntsc.pack(side=LEFT, padx=5, pady=2)
check_opengl.pack(side=LEFT, padx=5, pady=2)
check_fullscreen.pack(side=LEFT, padx=5, pady=2)
video_mode_frame.pack(fill=X, expand=1, pady=2)
# Geometry settings
video_geometry_frame = Frame(video_frame)
label_hint = Label(video_geometry_frame, text='If unsure, leave the following geometry settings to their default values (-1)')
label_width = Label(video_geometry_frame, text='Width :')
entry_width = Entry(video_geometry_frame, width=4, relief=SUNKEN, borderwidth=1, textvariable=self.VIDEO_WIDTH)
label_height = Label(video_geometry_frame, text='Height :')
entry_height = Entry(video_geometry_frame, width=4, relief=SUNKEN, borderwidth=1, textvariable=self.VIDEO_HEIGHT)
label_x_factor = Label(video_geometry_frame, text='X Factor :')
entry_x_factor = Entry(video_geometry_frame, width=4, relief=SUNKEN, borderwidth=1, textvariable=self.VIDEO_XFACTOR)
label_y_factor = Label(video_geometry_frame, text='Y Factor :')
entry_y_factor = Entry(video_geometry_frame, width=4, relief=SUNKEN, borderwidth=1, textvariable=self.VIDEO_YFACTOR)
label_hint.pack(side=TOP, padx=5, pady=2, fill=X, expand=1)
label_width.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
entry_width.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
label_height.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
entry_height.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
label_x_factor.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
entry_x_factor.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
label_y_factor.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
entry_y_factor.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
video_geometry_frame.pack(fill=X, expand=1, pady=2)
video_frame.pack(fill=X, expand=1, side=TOP, padx=5, pady=5)
def add_config_region_and_perfs_frame(self, master_frame):
"""
Add system and performances section widgets.
"""
perfs_frame = LabelFrame(master_frame, text='System and performances')
frame_region = Frame(perfs_frame)
label_region = Label(frame_region, text='Region :')
menu_region = OptionMenu(frame_region, self.REGION, "Auto", "Europe", "USA", "Japan")
frame_framerate = Frame(perfs_frame)
label_framerate = Label(frame_framerate, text='Frame rate :')
entry_framerate = Entry(frame_framerate, textvariable=self.FRAMERATE, width=2)
label_hz = Label(frame_framerate, text='Hz')
frame_sleep = Frame(perfs_frame)
label_sleep = Label(frame_sleep, text='Sleep ')
entry_sleep = Entry(frame_sleep, textvariable=self.SLEEP, width=2)
label_frames = Label(frame_sleep, text=' µseconds after each frames')
check_detach_terminal = Checkbutton(perfs_frame, text="Don't detach from terminal (MingW only)", variable=self.MINGW)
label_region.pack(side=LEFT, padx=5, pady=2, fill=X)
menu_region.pack(side=LEFT, padx=5, pady=2, fill=X, expand=1)
label_framerate.pack(side=LEFT, padx=5, pady=2, fill=X)
entry_framerate.pack(side=LEFT, pady=2, fill=X)
label_hz.pack(side=LEFT, padx=5, pady=2, fill=X)
label_sleep.pack(side=LEFT, padx=5, pady=2, fill=X)
entry_sleep.pack(side=LEFT, pady=2, fill=X)
label_frames.pack(side=LEFT, padx=5, pady=2, fill=X)
frame_region.pack(fill=X, expand=1, side=TOP)
frame_framerate.pack(fill=X, expand=1, side=LEFT)
frame_sleep.pack(fill=X, expand=1, side=LEFT)
check_detach_terminal.pack(fill=X, expand=1, side=RIGHT, padx=5, pady=2)
perfs_frame.pack(fill=BOTH, expand=1, side=TOP, padx=5, pady=5)
def refresh_list(self):
"""
Refresh main window ROM listbox
"""
refresh_rom_listbox(self)
def browse_for_dgen_exec(self):
self.DGEN_EXEC.set(tkFileDialog.askopenfilename(title='DGen executable', parent=self.config_window))
def browse_for_dgenrc(self):
self.DGEN_RC.set(tkFileDialog.askopenfilename(title='DGen configuration file (dgenrc)', parent=self.config_window))
def browse_for_rom_path(self):
self.DGEN_ROM_PATH.set(tkFileDialog.askdirectory(title='ROMs directory', parent=self.config_window))
def display_licence(self):
"""
Display licence window
"""
tkMessageBox.showinfo("pyDgen - Licence", LICENCE)
def display_about(self):
"""
Display about window
"""
tkMessageBox.showinfo("pyDgen - About", ABOUT)
########################################################################
def main():
"""
GUI construction and main loop
"""
main_window= Tk()
# Application icon base 64 encoded
b64_ico = """R0lGODlhQABAAOe9AABbzgBczwBd0ABfzABgzQBhzgBizxhcywRj0Bt
dzABl0ABm0gBn0wBo1ABqzxJmzQBs0RVnzgBt0wBu1BhozwFv1Rtp0AZv1glx0A5y0RJ
z0wB52BV01AB62QB72hh11Rt21h531yV71ROA2Sd81haB2il91yt+2C1/2TCC1jGD1zO
E2DWF2TaG2imK3TiH2yuL3zmI3DuL2T2M2j6N20CO3EGP3UOQ3kSR4EWS4UeT4lGT3FK
U3VSV3kuY4Fib3l6g5GWf3V+h5WGi5mii4Gmj4Wuk4myl422m5WSp5m6n5maq52+o53C
p6G6r4m+s43iq43Ct5Hmr5HGu5Xqs5nOv53Sw6HWx6Xux43ay6nyy5H2z5X605oC154G
26YK36oO464m45YS57Iq55ou654y76I286Y6964++7JHA7pLB75fB6ZrA75jC6prD65v
E7JzF7p3G757I8J/J8aXJ7KbK7afM7qnO8arP8rDQ7azR9LHR7rLS77PT8LTU8bXV87b
W9LfX9bvX77zY8L3Z8b7a8sDb88ba88Hd9Mjc9sLe9snd98Pf98ve+M7e8s/f88nh89D
g9Mri9NHh9czk9tPi9tTj+NXl+dbm+tfn+9zo9t3p997q+N/r+eDs+uHt/OTt9eLu/eX
u9+fv+Ojw+enx+vDy7+rz++v0/Oz1/e32//T38/L3+fP4+/T5/Pf59vX6/fj69/b7/vn
7+Pr8+ff9//v9+v78//j+///9+/n///z/+/7//ANo0ANo0ANo0ANo0ANo0ANo0ANo0AN
o0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0AN
o0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0AN
o0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0AN
o0ANo0ANo0ANo0ANo0ANo0ANo0ANo0ANo0CH5BAEKAP8ALAAAAABAAEAAAAj+AP8JHEi
woMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEM+5EWypMmTKEuOTMmyJUlWqWL
KnElTpiyTC03CqsmTJy6SuMAIGUK0qNGjQpCwOcSJFclZB0u28lQJTZGhR7MaFRKFFNA
WBsKKHUtWLAEAGqggusmrYElSY0oUIFC2LtkCGjIBdaEggt+/gAMDVpBAw5qfsQaSrCX
ohYG+giMLVtBB017IkjP7NWAYly2BtHjhciPBgObTEShbFh3Dbl0FsDFHMNDBD69Zq3L
xcqPB9F/Yrl2D0CuaSIsXyJMrV77ChIjSgA0wcUpyUAffqTmbOL68u/IWPD7+lUyFqrz
58+g/HfrTBUXqzSwakWy1A7MCCS7cHCqFvn//VLcAZcgeBBZo4IF79PEIL5Xw5ZcCENB
BUh0hQHYfFLi4MiCCHBoISCtApQDAiCSWaGICAHxARi2BdABZAlbUwgsQ9lHByyZIOGD
ijjxKgMllqEUnwRmpAFGAXwQwAUsmYD2Ywiel1PBYkINVBiSVSPaQShYE+FUAEa3YscG
LKyJhgWxUqgaUY8GJhZkBM3wSx3sG5EBKGRL0pYADiJyCwpttkvUBcbgMwUILiCaqKKI
rfACZASlA0oejs9kQyhl5pjbCJ3+AgFkKi4aqKAs6eFJSJ5WkWgkkiFSCSar+ryKCCBi
ZGrBCJXxo0JcBOFyaqQImhNLFrysEouqxyB6LCSxA0SEGGWKcscluYEALhh0kVSLCrrf
mumuvmOppwihU1IpEKnzgoe667LJrxx/MipZCAgQkoIAbqXQAAAFntUBSIy7O1q2us4H
767jl7tqEtho07PDDEKdgqmgORiDBGqmk8OgM/wZsK64E8+qruOTW2gQiecam8soKGBA
CoRVLgK/Gm3HMC8DcgvztyKkhbDIiGqRpJcWQyZzxxh3n7G3BPAM7ShU/Bx2kmkT7ZTT
Ns9mMs8A6Mx1uz0+bbAhhPI6YgG9U4xLzzEjf7PHAO3/tNNS7LkFKuu2yG0f+fakNrXb
RbNecNNdLiyz3uHTPpkQpiBDi+OOPV8LLFKalvfbRgrutdMgGk5y4AU1IsgIJJpRueuk
08NJE5X7HjDHWcA7+ceGdg/35ySmzrIAGqrO+2t9Wq5FKCW1vPTvnTY87RdSZgdB737/
HLHzAWcsOt9cHl7yrE0Bn9sHzlhctPMHVa0448oePAoW5Y6MIwJGAfb869FdKoMYpELR
NybYCP0J78qNoQv5m8wNWaMJVmehCYHg3v/AF7xQOsJAH1oCHJ9CpBIf4X/qiMEAFkAA
O68pDEhYIvtaJD4KYsUDDMjWbFDSCD5Qy3ME+YQdKRUCFD8MACRtowge+5zf+sPnLx+g
QwYKN4gwY0JMCBuEJD9hnZTv0Xf3u9wA0BaZOoOhClyJAACTUYg3kI8AZeFGEIqKGgVK
smsWoaMXoFIEUQ4BfkmCBiSb1zROnWEEbBYNG+qlRAmkoRctcQ4AYbAIQNkxAF0Tjgzc
ZwRWj4EGgDAABXighjcCLAATCIIspTCEKUHiCKEXZhCsIgkEzQJsG9ECSNJAvNURwBS4
4kYVR2tKWWsBFEPTUQ00u0iU3ooIIoiMDSsyHB2g6wRZAAcyS/ICX0cOMAyRAzWpas5o
WGIwBuiCj0OyBen+Z5jXHic0H9RJLV1wBItryD5Kc4ZXorFI04zkYCcyBJAJ0Kck79yi
0edKzb3XA50BeQRI7eOqf5vydDPiZGcpooRMCJUhJAsjCeKYtDl3IqEY3ylGNksERJYF
KVEqCCzpkFAsdTelG0YCKZrp0FqZYyCxcStOamsQWiZHIKmyKEpH49KdADapQh0rUohr
1qEhNqlITEhAAOw=="""
ico = PhotoImage(data=b64_ico)
main_window.wm_title('pyDGen - A simple graphical interface to the DGen SEGA GENESIS emulator')
main_window.tk.call('wm', 'iconphoto', main_window._w, ico)
gui = PyDGenGui(main_window)
# Set main window start geometry to 80% max screen resolution
gui.master.geometry(str(int(round(main_window.winfo_screenwidth()/1.25)))+
'x'
+str(int(round(main_window.winfo_screenheight()/1.25))))
if not os.path.isdir(os.path.join(get_home_dir(), '.dgen')):
gui.display_config_window()
refresh_rom_listbox(gui)
gui.mainloop()
try :
gui.destroy()
except:
pass
if __name__ == "__main__" :
"""
Entry point
"""
main()