1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2002 Martin Willemoes Hansen <mwh@sysrq.dk>
5# Copyright (C) 2019 Dawid Gan <deveee@gmail.com>
6#
7# Jump 'n Bump is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# Jump 'n Bump is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21import sys
22import os
23import tempfile
24import shutil
25import gettext
26
27import gi
28gi.require_version('Gtk', '3.0')
29from gi.repository import Gtk, Gdk, GObject
30
31RESOURCE_DIR = '%%DATADIR%%/jumpnbump'
32BINARY_DIR = '%%BINDIR%%'
33UNPACK_BIN = '%%UNPACKBIN%%'
34
35application = "jumpnbump-menu"
36gettext.install(application)
37
38
39def populate_treeview():
40    levels = []
41    add_levels(levels, RESOURCE_DIR)
42    add_levels(levels, os.path.expanduser("~/.jumpnbump/levels"))
43
44    levels = sorted(levels, key=lambda level: level[0])
45
46    COLUMN_LEVEL = 0
47    COLUMN_DIR = 1
48    store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING)
49
50    for level in levels:
51        iter = store.append()
52        store.set(iter, COLUMN_LEVEL, level[0], COLUMN_DIR, level[1])
53
54    treeview.set_model(store)
55
56    renderer = Gtk.CellRendererText()
57    treeview.append_column(Gtk.TreeViewColumn(
58        _('Level'), renderer, text=COLUMN_LEVEL))
59
60
61def add_levels(levels, dir):
62    try:
63        for file in os.listdir(dir):
64            if (file.endswith('.dat') or file.endswith('.DAT')):
65                levels.append((file, dir))
66    except OSError as err:
67        print("%s not found (%s)." % (dir,  str(err)))
68
69
70def standalone_mode(widget):
71    disable_enable_level(1)
72    disable_enable_server(0)
73    num_clients.set_sensitive(0)
74    nogore.set_sensitive(1)
75    noflies.set_sensitive(1)
76
77
78def client_mode(widget):
79    disable_enable_level(1)
80    disable_enable_server(1)
81    num_clients.set_sensitive(0)
82    nogore.set_sensitive(1)
83    noflies.set_sensitive(1)
84
85
86def server_mode(widget):
87    disable_enable_level(1)
88    disable_enable_server(0)
89    num_clients.set_sensitive(1)
90    nogore.set_sensitive(1)
91    noflies.set_sensitive(1)
92
93
94def disable_enable_server(setting):
95    server_entry.set_sensitive(setting)
96    player_num.set_sensitive(setting)
97
98
99def disable_enable_level(setting):
100    treeview.set_sensitive(setting)
101    mirror.set_sensitive(setting)
102    if (not setting):
103        mirror.set_active(setting)
104
105
106def level_changed(widget):
107    model, iter = treeview.get_selection().get_selected()
108    global choosen_level
109    choosen_level = '%s/%s' % (model.get_value(iter, 1),
110                               model.get_value(iter, 0))
111    unpackdir = None
112    try:
113        unpackdir = tempfile.mkdtemp("", "jumpnbump-menu-")
114        os.chdir(unpackdir)
115        os.spawnlp(os.P_WAIT, UNPACK_BIN,
116                   'unpack', choosen_level)
117        os.spawnlp(os.P_WAIT, 'convert', 'convert', '-scale',
118                   '50%', 'level.pcx', 'level_scaled.pcx')
119        os.spawnlp(os.P_WAIT, 'convert', 'convert',
120                   'level_scaled.pcx', 'level.png')
121        image.set_from_file('level.png')
122    except Exception as err:
123        print(err)
124    finally:
125        if unpackdir != None:
126            shutil.rmtree(unpackdir)
127
128    image.show()
129
130
131def about(widget):
132    global about_dialog
133    if (not about_dialog.is_visible()):
134        about_dialog.show_all()
135
136
137def about_close(widget):
138    global about_dialog
139    about_dialog.hide()
140
141def about_quit(widget, event):
142    global about_dialog
143    about_dialog.hide()
144    return True
145
146
147def run(widget):
148    if (standalone.get_active()):
149        execute(*get_level() + common_options())
150    elif (client.get_active()):
151        execute('-player', str(player_num.get_value_as_int()),
152                '-connect', server_entry.get_text(),
153                *get_level() + common_options())
154    else:
155        execute('-server', str(num_clients.get_value_as_int()),
156                *get_level() + common_options())
157
158
159def get_level():
160    level = []
161    if (mirror.get_active()):
162        level.append('-mirror')
163    else:
164        level.append('-dat')
165        level.append(choosen_level)
166
167    return level
168
169
170def common_options():
171    options = []
172
173    if (fullscreen.get_active()):
174        options.append('-fullscreen')
175    if (nogore.get_active()):
176        options.append('-nogore')
177    if (double_res.get_active()):
178        options.append('-scaleup')
179    if (nosound.get_active()):
180        options.append('-nosound')
181    if (noflies.get_active()):
182        options.append('-noflies')
183    if (withmusic.get_active()):
184        options.append('-musicnosound')
185    if (mirror.get_active()):
186        options.append('-mirror')
187
188    return options
189
190
191def execute(*cmd):
192    try:
193        os.spawnl(os.P_NOWAIT, BINARY_DIR + '/jumpnbump', 'jumpnbump', *cmd)
194    except Exception as err:
195        print(err)
196
197
198def main():
199    global gladefile
200
201    global_gladefile = RESOURCE_DIR + '/jumpnbump_menu.glade'
202    local_gladefile = './jumpnbump_menu.glade'
203
204    if (os.access(global_gladefile, os.R_OK)):
205        gladefile = global_gladefile
206        del local_gladefile
207    elif (os.access(local_gladefile, os.R_OK)):
208        gladefile = local_gladefile
209        del global_gladefile
210    else:
211        print('Could not find the glade file')
212        return 0
213
214    gui = Gtk.Builder()
215    gui.add_from_file(gladefile)
216
217    global about_dialog, choosen_level, standalone, client, server, treeview, \
218        mirror, num_clients, server_entry, player_num, fullscreen, \
219        nogore, double_res, nosound, noflies, withmusic, image
220
221    choosen_level = ''
222    standalone = gui.get_object('standalone')
223    client = gui.get_object('client')
224    server = gui.get_object('server')
225    treeview = gui.get_object('level_treeview')
226    populate_treeview()
227    mirror = gui.get_object('mirror')
228    num_clients = gui.get_object('num_of_clients')
229    server_entry = gui.get_object('server_entry')
230    player_num = gui.get_object('player_num')
231    fullscreen = gui.get_object('fullscreen')
232    nogore = gui.get_object('nogore')
233    double_res = gui.get_object('double_res')
234    nosound = gui.get_object('nosound')
235    noflies = gui.get_object('noflies')
236    withmusic = gui.get_object('withmusic')
237    image = gui.get_object('image')
238    about_dialog = gui.get_object('about')
239
240    gui.connect_signals({'standalone_mode': standalone_mode,
241                         'client_mode': client_mode,
242                         'server_mode': server_mode,
243                         'level_changed': level_changed,
244                         'quit': lambda *args: Gtk.main_quit(),
245                         'run': run,
246                         'about': about,
247                         'about_ok': about_close,
248                         'about_quit': about_quit})
249
250    Gtk.main()
251
252if __name__ == '__main__':
253    main()
254