1import math 2import os 3import random 4 5# from numpy import array, float32 6from fsgs.platform import PlatformHandler 7from arcade.glui.constants import TOP_ITEM_ARROW, TOP_ITEM_LEFT 8from arcade.glui.constants import ROW_NAME, ROW_PLATFORM 9 10from fsbc.util import memoize 11from fsgs.Database import Database 12from fsgs.util.gamenameutil import GameNameUtil 13from arcade.resources import resources, gettext 14from .font import BitmapFont 15from .opengl import gl, fs_emu_blending, fs_emu_texturing 16from .render import Render 17from arcade.glui.state import State 18from arcade.glui.texture import Texture 19from arcade.glui.texturemanager import TextureManager 20from .errordialog import show_exception 21 22LIGHTING = False 23DIRTY_WHILE_NOT_LOADED = True 24 25 26def create_item_menu(name): 27 from arcade.glui.itemmenu import ItemMenu 28 29 return ItemMenu(name) 30 31 32def create_mesh(width, height, upsidedown): 33 hw = width / 2 34 hh = height / 2 35 if LIGHTING: 36 res = 200 37 radius = 0.018 38 else: 39 res = 2 40 radius = 0.0 41 # radius = 0.1 42 mesh = [] 43 for y in range(0, res + 1): 44 y = -hh + y * height / res 45 line = [] 46 mesh.append(line) 47 for x in range(0, res + 1): 48 x = -hw + x * width / res 49 # d = min(x, y, res- x, res -y) / (res / 2.0) 50 # rx = x * 2.0 / res 51 z = radius 52 nx = 0.0 53 ny = 0.0 54 nz = 1.0 55 # edge = False 56 # using max to account for rounding errors - d cannot be negative 57 d = max(0, hw - abs(x)) 58 # print(hw, x, d) 59 if d < radius: 60 d = radius - d 61 # d = radius - 1.0 - x 62 # print(d, d / radius) 63 a = math.acos(d / radius) 64 # print(a) 65 z = math.sin(a) * radius 66 nx = -d if x < 0 else d 67 nz = z 68 # edge = True 69 # using max to account for rounding errors - d cannot be negative 70 d = max(0, hh - abs(y)) 71 if d < radius: 72 d = radius - d 73 # d = radius - 1.0 - x 74 # print(d, d / radius) 75 a = math.acos(d / radius) 76 # print(a) 77 z = min(z, math.sin(a) * radius) 78 ny = -d if y < 0 else d 79 nz = z 80 # edge = True 81 nl = math.sqrt(nx ** 2 + ny ** 2 + nz ** 2) 82 nx /= nl 83 ny /= nl 84 nz /= nl 85 line.append((x, y, z, nx, ny, nz)) 86 # if edge: 87 # line.append((x, y, -z, nx, ny, -nz)) 88 return mesh 89 90 91# @memoize 92# def get_vbo_for_cover(width, height): 93# floats = [] 94# upsidedown=False 95# mesh = create_mesh(width, height, upsidedown) 96# hw = width / 2 97# hh = height / 2 98# mesh_size = len(mesh) 99# print(mesh_size) 100# count = 0 101# for iy in range(mesh_size - 1): 102# for ix in range(mesh_size - 1): 103# def vertex(ix, iy): 104# x, y, z, nx, ny, nz = mesh[iy][ix] 105# if upsidedown: 106# s, t = (x + hw) / width, (y + hh) / height 107# else: 108# s, t = (x + hw) / width, 1.0 - (y + hh) / height 109# floats.extend([s, t, nx, ny, nz, x, y, z]) 110# vertex(ix, iy) 111# vertex(ix + 1, iy) 112# vertex(ix + 1, iy + 1) 113# vertex(ix, iy + 1) 114# count += 4 115# # FIXME: specify usage with usage kw arg (specify as "static" data, 116# # used often) 117# vbo = VBO(array(floats, dtype=float32)) 118# return vbo, count 119 120 121def render_cover(texture, width, height): 122 if not texture: 123 texture = Texture.missing_cover 124 texture.bind() 125 x = -width / 2.0 126 # y = height / 2.0 - 0.5 - height / 2.0 127 y = -0.5 128 # if reflection: 129 130 gl.glBegin(gl.GL_QUADS) 131 gl.glColor3f(0.33, 0.33, 0.33) 132 gl.glTexCoord2f(0.0, 1.0) 133 gl.glVertex2f(x, y) 134 gl.glTexCoord2f(1.0, 1.0) 135 gl.glVertex2f(x + width, y) 136 gl.glTexCoord2f(1.0, 0.0) 137 gl.glVertex2f(x + width, y - height) 138 gl.glTexCoord2f(0.0, 0.0) 139 gl.glVertex2f(x, y - height) 140 gl.glEnd() 141 142 # else: 143 gl.glBegin(gl.GL_QUADS) 144 gl.glColor3f(1.0, 1.0, 1.0) 145 gl.glTexCoord2f(0.0, 1.0) 146 gl.glVertex2f(x, y) 147 gl.glTexCoord2f(1.0, 1.0) 148 gl.glVertex2f(x + width, y) 149 gl.glTexCoord2f(1.0, 0.0) 150 gl.glVertex2f(x + width, y + height) 151 gl.glTexCoord2f(0.0, 0.0) 152 gl.glVertex2f(x, y + height) 153 gl.glEnd() 154 155 # glPushMatrix() 156 # glTranslatef(0.0, height / 2.0 - 0.5, 0.0) 157 # vbo, count = get_vbo_for_cover(width, height) 158 # vbo.bind() 159 # glInterleavedArrays(GL_T2F_N3F_V3F, 0, None) 160 # if reflection: 161 # glScalef(1.0, -1.0, 1.0) 162 # glTranslatef(0.0, height, 0.0) 163 # if LIGHTING: 164 # # No point in drawing everything 165 # glDrawArrays(GL_QUADS, 0, count // 4) 166 # else: 167 # glDrawArrays(GL_QUADS, 0, count) 168 # else: 169 # glDrawArrays(GL_QUADS, 0, count) 170 # vbo.unbind() 171 # glPopMatrix() 172 173 174class MenuItem(object): 175 def __init__(self, title=""): 176 self.ratio = 0.75 177 self.title = title 178 self.supertitle = "" 179 self.subtitle = "" 180 self.path_title = title 181 self.path_title_active = "" 182 # x, y, w and h are used by top menu items 183 self.x = 0 184 self.y = 0 185 self.w = 0 186 self.h = 0 187 self.enabled = True 188 189 def activate(self, menu): 190 print("Activate not overidden") 191 192 @memoize 193 def get_image_files(self): 194 path = self.get_image_path() 195 if path: 196 return [path] 197 return [] 198 199 def get_default_texture(self): 200 return Texture.default_item 201 202 def get_texture(self): 203 path = self.get_image_path() 204 if path: 205 try: 206 texture = TextureManager.get().get_texture(path) 207 except KeyError: 208 texture = None 209 if texture: 210 if texture is Texture.default_item: 211 return Texture.missing_cover 212 return texture 213 else: 214 Render.get().dirty = DIRTY_WHILE_NOT_LOADED 215 return Texture.missing_cover 216 # return self.get_default_texture() 217 218 @memoize 219 def get_image_path(self): 220 name = self.get_resource_name() 221 if name: 222 try: 223 return resources.resource_filename( 224 os.path.join(u"items", name + u".png") 225 ) 226 except Exception: 227 pass 228 return None 229 230 def get_resource_name(self): 231 return None 232 233 def render( 234 self, 235 width, 236 height, 237 border_color=(0.0, 0.0, 0.0, 1.0), 238 inner_border_color=(0.92, 0.92, 0.92), 239 height_offset=0.0, 240 brightness=1.0, 241 ratio=None, 242 area=None, 243 ): 244 render_cover(self.get_texture(), width, height) 245 246 def create_menu_path(self, menu): 247 path = [] 248 # while menu: 249 if not menu: 250 return path 251 path.append(menu.selected_item) 252 for p in reversed(menu.parents): 253 # path.insert(0, menu.selected_item) 254 path.insert(0, p.selected_item) 255 # menu = menu.parent_menu 256 print("create_menu_path", path) 257 for p in path: 258 print(p.path_title) 259 return path 260 261 def get_category_filter(self): 262 return None 263 264 def get_game_filter(self): 265 return None 266 267 def generate_category_filters(self, menu_path): 268 for item in menu_path: 269 flt = item.get_category_filter() 270 if flt: 271 yield flt 272 273 def generate_game_filters(self, menu_path): 274 for item in menu_path: 275 flt = item.get_game_filter() 276 if flt: 277 yield flt 278 279 def generate_category_items(self, menu_path): 280 category_filters = list(self.generate_category_filters(menu_path)) 281 282 def check(cat): 283 for flt in category_filters: 284 if not flt(cat): 285 return False 286 return True 287 288 categories = [ 289 PlatformMenuItem(), 290 LetterMenuItem(), 291 ShuffleMenuItem(), 292 ListMenuItem(), 293 # YearMenuItem(), 294 # KeywordMenuItem(), 295 ] 296 if len(menu_path) == 0: 297 categories.insert(0, AllMenuItem()) 298 return [item for item in categories if check(item)] 299 300 def generate_game_items(self, menu_path): 301 args = [] 302 clause = [] 303 list_uuid = None 304 for item in menu_path: 305 c, a = item.get_filter_clause() 306 if c: 307 clause.append(c) 308 args.extend(a) 309 if item.get_filter_list_uuid(): 310 list_uuid = item.get_filter_list_uuid() 311 # clause = " ".join(clause) 312 313 filters = list(self.generate_game_filters(menu_path)) 314 print("filters for", menu_path, "-", filters) 315 return self.create_game_items(clause, args, filters, list_uuid) 316 317 @classmethod 318 def create_game_items(cls, words, args=None, filters=None, list_uuid=None): 319 if filters is None: 320 filters = [] 321 search = " ".join(words) 322 item_list = [] 323 local_game_database = Database.instance() 324 for game in local_game_database.find_games_new( 325 search=search, database_only=True, list_uuid=list_uuid 326 ): 327 item = GameItem(game) 328 for filter in filters: 329 pass 330 # if not filter(game): 331 # break 332 else: 333 item_list.append(item) 334 return item_list 335 336 def get_filter_clause(self): 337 return "", [] 338 339 def get_filter_list_uuid(self): 340 return None 341 342 def update_size_left(self): 343 self.update_size(self.get_top_left_text()) 344 345 def update_size_right(self): 346 self.update_size(self.get_top_right_text()) 347 348 def update_size(self, text): 349 # w, h = Render.get().measure_text(text, Font.main_path_font) 350 w, h = BitmapFont.title_font.measure(text) 351 self.w = w + 20 + 20 352 353 def get_top_left_text(self): 354 return self.path_title_active.upper() or self.path_title.upper() 355 356 def get_top_right_text(self): 357 return self.title.upper() 358 359 def render_top_background( 360 self, 361 selected, 362 style=TOP_ITEM_ARROW, 363 mouse_state=False, 364 mouse_pressed_state=False, 365 ): 366 x, y, w, h = self.x, self.y, self.w, self.h 367 z = -0.01 - 0.01 * x / 1920 368 selected = selected or mouse_state 369 if selected: 370 fs_emu_texturing(True) 371 fs_emu_blending(True) 372 if mouse_pressed_state: 373 alpha = 0.75 374 else: 375 alpha = 1.0 376 Texture.top_item_background.render(x, y, w, h, z, opacity=alpha) 377 # fs_emu_texturing(False) 378 # fs_emu_blending(False) 379 # gl.glBegin(gl.GL_QUADS) 380 # gl.glColor3f(0.00, 0x99 / 0xff, 0xcc / 0xff) 381 # gl.glVertex3f(x + 4, y + 4, z) 382 # gl.glVertex3f(x + w - 4, y + 4, z) 383 # gl.glVertex3f(x + w - 4, y + h - 4, z) 384 # gl.glVertex3f(x + 4, y + h - 4, z) 385 # gl.glEnd() 386 387 # fs_emu_blending(True) 388 # glColor3f(1.0, 1.0, 1.0) 389 # 390 # #z = -0.01 391 # 392 # if style == TOP_ITEM_LEFT: 393 # if selected: 394 # texture = Texture.top_item_left_selected 395 # else: 396 # texture = Texture.top_item_left 397 # texture.bind() 398 # tw, th = texture.size 399 # w = 60 400 # 401 # glBegin(GL_QUADS) 402 # glTexCoord2f(0.0, 1.0) 403 # glVertex3f(x, y, z) 404 # glTexCoord2f(1.0, 1.0) 405 # glVertex3f(x + w, y, z) 406 # glTexCoord2f(1.0, 0.0) 407 # glVertex3f(x + w, y + h, z) 408 # glTexCoord2f(0.0, 0.0) 409 # glVertex3f(x, y + h, z) 410 # glEnd() 411 # x += 60 412 # 413 # w = self.w - 60 414 # 415 # if selected: 416 # texture = Texture.top_item_selected 417 # x = x - 60 418 # w = w + 60 419 # else: 420 # texture = Texture.top_item 421 # texture.bind() 422 # tw, th = texture.size 423 # glBegin(GL_QUADS) 424 # glTexCoord2f(0.0, 1.0) 425 # glVertex3f(x, y, z) 426 # glTexCoord2f(1.0, 1.0) 427 # glVertex3f(x + w, y, z) 428 # glTexCoord2f(1.0, 0.0) 429 # glVertex3f(x + w, y + h, z) 430 # glTexCoord2f(0.0, 0.0) 431 # glVertex3f(x, y + h, z) 432 # glEnd() 433 # 434 # if style == TOP_ITEM_ARROW or style == TOP_ITEM_RIGHT: 435 # if style == TOP_ITEM_ARROW: 436 # if selected: 437 # texture = Texture.top_item_arrow_selected 438 # else: 439 # texture = Texture.top_item_arrow 440 # texture.bind() 441 # tw, th = texture.size 442 # else: 443 # Texture.top_item_right.bind() 444 # tw, th = Texture.top_item_right.size 445 # x = x + w 446 # w = 60 447 # 448 # glBegin(GL_QUADS) 449 # glTexCoord2f(0.0, 1.0) 450 # glVertex3f(x, y, z) 451 # glTexCoord2f(1.0, 1.0) 452 # glVertex3f(x + w, y, z) 453 # glTexCoord2f(1.0, 0.0) 454 # glVertex3f(x + w, y + h, z) 455 # glTexCoord2f(0.0, 0.0) 456 # glVertex3f(x, y + h, z) 457 # glEnd() 458 # 459 # fs_emu_blending(False) 460 461 def render_top_left(self, selected=False): 462 state = State.get() 463 mouse_state = state.mouse_item == self 464 mouse_pressed_state = mouse_state and state.mouse_press_item == self 465 self.render_top_background( 466 selected, 467 style=TOP_ITEM_LEFT, 468 mouse_state=mouse_state, 469 mouse_pressed_state=mouse_pressed_state, 470 ) 471 text = self.get_top_left_text() 472 self.render_top(text, selected) 473 474 def render_top_right(self, selected=False): 475 state = State.get() 476 mouse_state = state.mouse_item == self 477 mouse_pressed_state = mouse_state and state.mouse_press_item == self 478 self.render_top_background( 479 selected, 480 style=TOP_ITEM_LEFT, 481 mouse_state=mouse_state, 482 mouse_pressed_state=mouse_pressed_state, 483 ) 484 self.render_top(self.get_top_right_text(), selected) 485 486 def render_top(self, text="", selected=False, right_align=False): 487 if right_align: 488 tw, th = BitmapFont.title_font.measure(text) 489 x = self.x + self.w - 20 - tw 490 else: 491 x = self.x + 20 492 # if selected: 493 # color = (0.0, 0.0, 0.0) 494 # else: 495 # color = (0.85, 0.85, 0.85) 496 # tw, th = Render.get().text(text, Font.main_path_font, 497 # x, self.y + 30, h=TOP_HEIGHT_VISIBLE, color=color) 498 BitmapFont.title_font.render(text, x, self.y + 14) 499 # print(BitmappedFont.title_font.h) 500 501 def get_screen_texture(self, n): 502 return Texture.missing_screenshot 503 # return None 504 505 506class AutoExpandItem(MenuItem): 507 def activate(self, menu): 508 new_menu = create_item_menu(self.title) 509 menu_path = self.create_menu_path(menu) 510 new_menu.update_path(menu_path) 511 want_selected = len(new_menu) 512 for item in self.generate_game_items(menu_path): 513 new_menu.append(item) 514 if len(new_menu) > want_selected: 515 new_menu.set_selected_index(want_selected, immediate=True) 516 new_menu.add_add_item() 517 return new_menu 518 519 520class DummyItem(MenuItem): 521 def __init__(self, title): 522 MenuItem.__init__(self) 523 self.title = title 524 525 526class NoItem(MenuItem): 527 def __init__(self, title="N/A"): 528 MenuItem.__init__(self) 529 self.title = title 530 531 def get_default_texture(self): 532 return Texture.missing_cover 533 534 def activate(self, menu): 535 return 536 537 538class NoLastPlayedItem(MenuItem): 539 def __init__(self): 540 MenuItem.__init__(self) 541 self.title = gettext("No Last Played") 542 543 544class ListMenuItem(MenuItem): 545 def __init__(self): 546 MenuItem.__init__(self) 547 self.title = gettext("List") 548 self.path_title_active = "Choose List" 549 550 def get_resource_name(self): 551 return "list" 552 553 def activate(self, menu): 554 print("ListMenuItem.activate") 555 new_menu = create_item_menu(gettext("Select List")) 556 menu_path = self.create_menu_path(menu) 557 new_menu.update_path(menu_path) 558 # Database.cursor.execute("SELECT DISTINCT platform FROM Game " 559 # "ORDER BY platform") 560 # items = [] 561 # for row in Database.cursor: 562 # item = PlatformItem(row[0]) 563 # items.append((item.sort_title, item)) 564 # for sort_title, item in sorted(items): 565 # new_menu.append(item) 566 567 for title, path in ListItem.get_game_lists(): 568 item = ListItem(title, path) 569 new_menu.append(item) 570 return new_menu 571 572 573class ListItem(AutoExpandItem): 574 def __init__(self, title, path): 575 AutoExpandItem.__init__(self) 576 self.list_path = path 577 self.title = title 578 # self.platform = platform 579 self.path_title = title 580 self.subtitle = "" 581 self.sort_title = self.title 582 # self.game_filter_set = set() 583 self._list = None 584 585 @classmethod 586 def get_game_lists(cls): 587 result = [] 588 local_game_database = Database.instance() 589 for list_uuid, list_name in local_game_database.get_game_lists(): 590 result.append((list_name, list_uuid)) 591 return result 592 593 @classmethod 594 def get_favorites_uuid(cls): 595 for list_name, list_uuid in cls.get_game_lists(): 596 # FIXME: Should use some common function to determine this 597 if list_name == "Favorites": 598 return list_uuid 599 raise LookupError("No favorites list") 600 601 def get_category_filter(self): 602 def category_filter(category): 603 if category.__class__ == ListMenuItem: 604 return False 605 return True 606 607 return category_filter 608 609 def get_filter_list_uuid(self): 610 return self.list_path 611 612 # @memoize 613 614 # def get_list_contents(self): 615 # if self._list is None: 616 # self._list = set() 617 # local_game_database = Database.instance() 618 # local_game_database.get 619 # return self._list 620 621 # def get_game_filter(self): 622 # 623 # def game_filter(game): 624 # game_uuids = set() 625 # if game.uuid in game_uuids: 626 # return True 627 # return False 628 # 629 # return game_filter 630 631 # def get_filter_clause(self): 632 # # FIXME: NUMBERS 633 # return "AND platform = ?", [self._platform] 634 # print(name) 635 636 637class PlatformMenuItem(MenuItem): 638 def __init__(self): 639 MenuItem.__init__(self) 640 self.title = gettext("Platform") 641 self.path_title_active = "Choose Platform" 642 643 def get_resource_name(self): 644 return "platform" 645 646 def activate(self, menu): 647 print("PlatformMenuItem.activate") 648 new_menu = create_item_menu(gettext("Select Platform")) 649 menu_path = self.create_menu_path(menu) 650 new_menu.update_path(menu_path) 651 with Database.instance() as database: 652 cursor = database.cursor() 653 cursor.execute( 654 "SELECT DISTINCT platform FROM game WHERE have >= 3 " 655 "ORDER BY platform" 656 ) 657 items = [] 658 for row in cursor: 659 if not row[0]: 660 continue 661 item = PlatformItem(row[0]) 662 items.append((item.sort_title, item)) 663 for sort_title, item in sorted(items): 664 new_menu.append(item) 665 return new_menu 666 667 668class PlatformItem(AutoExpandItem): 669 def __init__(self, platform): 670 AutoExpandItem.__init__(self) 671 self._platform = platform 672 # using self.platform for "description" 673 674 # self.sort_title = "" 675 # try: 676 # self.title, self.subtitle, self.sort_title = { 677 # "Amiga": ( 678 # "Amiga", 679 # "Commodore Amiga", 680 # "Commodore 2"), 681 # "amstrad-cpc": ( 682 # "Amstrad CPC", 683 # "Color Personal Computer", 684 # ""), 685 # "apple-II": ( 686 # "Apple II", 687 # "II/Plus/e/c", 688 # ""), 689 # "arcade": ( 690 # "Arcade Video Games", 691 # "Multiple Arcade Machines", 692 # ""), 693 # "atari-2600": ( 694 # "Atari 2600", 695 # "Video Computer System", 696 # "Atari 1"), 697 # "atari-8-bit": ( 698 # "Atari 8-Bit", 699 # "Atari 400/800/XL/XE", 700 # "Atari 2"), 701 # "atari-5200": ( 702 # "Atari 5200", 703 # "Atari 5200 SuperSystem", 704 # "Atari 3"), 705 # "atari-7800": ( 706 # "Atari 7800", 707 # "Atari 7800 ProSystem", 708 # "Atari 4"), 709 # "atari-st": ( 710 # "Atari ST", 711 # "Atari ST/STE", 712 # "Atari 5"), 713 # "bbc-micro": ( 714 # "BBC Micro", 715 # "Acorn BCC Micro", 716 # ""), 717 # "commodore-64": ( 718 # "Commodore 64", 719 # "", 720 # "Commodore 1"), 721 # "game-boy": ( 722 # "Game Boy", 723 # "", 724 # "Nintendo 2"), 725 # "game-boy-color": ( 726 # "Game Boy Color", 727 # "", 728 # "Nintendo 5"), 729 # "game-boy-advance": ( 730 # "Game Boy Advance", 731 # "", 732 # "Nintendo 6"), 733 # "game-cube": ( 734 # "GameCube", 735 # "Nintendo GameCube", 736 # "Nintendo 7"), 737 # "game-gear": ( 738 # "Game Gear", 739 # "Sega Game Gear", 740 # "Sega 3"), 741 # "lynx": ( 742 # "Atari Lynx", 743 # "", 744 # "Atari 6"), 745 # "nintendo": ( 746 # "Nintendo", 747 # "", 748 # "Nintendo 1"), 749 # "nintendo-64": ( 750 # "Nintendo 64", 751 # "", 752 # "Nintendo 4"), 753 # "DOS": ( 754 # "DOS", 755 # "IBM PC Compatible", 756 # "PC 1"), 757 # "master-system": ( 758 # "Master System", 759 # "Sega Master System / Mark III", 760 # "Sega 1"), 761 # "mega-drive": ( 762 # "Mega Drive", 763 # "Sega Mega Drive / Genesis", 764 # "Sega 2"), 765 # "playstation": ( 766 # "PlayStation", 767 # "Sony PlayStation", 768 # ""), 769 # "playstation-2": ( 770 # "PlayStation 2", 771 # "Sony PlayStation 2", 772 # ""), 773 # "super-nintendo": ( 774 # "Super Nintendo", 775 # "Entertainment System", 776 # "Nintendo 3"), 777 # "turbografx-16": ( 778 # "TurboGrafx-16", 779 # "Entertainment SuperSystem", 780 # ""), 781 # "wii": ( 782 # "Wii", 783 # "Nintendo Wii", 784 # "Nintendo 8"), 785 # "windows": ( 786 # "Windows", 787 # "IBM PC Compatible", 788 # "PC 2"), 789 # "zx-spectrum": ( 790 # "ZX Spectrum", 791 # "Sinclair ZX Spectrum", 792 # ""), 793 # }[platform] 794 # except KeyError: 795 # self.title = platform 796 self.platform = platform 797 self.path_title = platform 798 799 self.title = PlatformHandler.get_platform_name(platform) 800 self.path_title = self.title 801 802 # self.title = "" 803 # self.title = "CHOOSE PLATFORM: " + self.title 804 # self.subtitle = "" 805 self.subtitle = "CHOOSE PLATFORM" 806 807 # if not self.sort_title: 808 # self.sort_title = self.title 809 810 # FIXME: for now, test using just title as sort title 811 self.sort_title = self.title 812 813 def get_category_filter(self): 814 def category_filter(category): 815 if category.__class__ == PlatformMenuItem: 816 return False 817 return True 818 819 return category_filter 820 821 # def get_game_filter(self): 822 # def game_filter(game_info): 823 # if game_info.platform == self._platform: 824 # return True 825 # return False 826 # return game_filter 827 828 @memoize 829 def get_resource_name(self): 830 name = "" 831 for c in self._platform.lower(): 832 if c in "abcdefghijklmnopqrstuvwxyz0123456789": 833 name = name + c 834 return name 835 836 def get_filter_clause(self): 837 return "s:{0}".format(self._platform), [] 838 839 840class AllMenuItem(AutoExpandItem): 841 def __init__(self): 842 AutoExpandItem.__init__(self) 843 self.title = gettext("All") 844 self.path_title = "All Games" 845 846 def get_resource_name(self): 847 return "all" 848 849 def get_category_filter(self): 850 def category_filter(category): 851 return False 852 853 return category_filter 854 855 # def get_game_filter(self): 856 # def game_filter(game_info): 857 # return True 858 # return game_filter 859 860 861class ShuffleMenuItem(MenuItem): 862 def __init__(self): 863 MenuItem.__init__(self) 864 self.title = gettext("Shuffle") 865 self.path_title = "Shuffle" 866 # FIXME: Sort game list in random order 867 868 def get_resource_name(self): 869 return "shuffle" 870 871 def activate(self, menu): 872 new_menu = create_item_menu(self.title) 873 menu_path = self.create_menu_path(menu) 874 new_menu.update_path(menu_path) 875 game_items = list(self.generate_game_items(menu_path)) 876 random.shuffle(game_items) 877 for item in game_items: 878 new_menu.append(item) 879 return new_menu 880 881 882class LetterMenuItem(MenuItem): 883 def __init__(self): 884 MenuItem.__init__(self) 885 self.title = gettext("Letter") 886 self.path_title_active = "Choose Letter" 887 888 def get_resource_name(self): 889 return "letter" 890 891 def activate(self, menu): 892 print("LetterMenuItem.activate") 893 new_menu = create_item_menu(gettext("Select First Letter")) 894 menu_path = self.create_menu_path(menu) 895 new_menu.update_path(menu_path) 896 for letter in "#ABCDEFGHIJKLMNOPQRSTUVWXYZ": 897 item = LetterItem(letter) 898 new_menu.append(item) 899 return new_menu 900 901 902class LetterItem(AutoExpandItem): 903 def __init__(self, letter): 904 AutoExpandItem.__init__(self) 905 self.letter = letter 906 self.title = letter 907 self.path_title = letter 908 909 def get_resource_name(self): 910 if self.letter == "#": 911 return "0" 912 return self.letter.lower() 913 914 def get_category_filter(self): 915 def category_filter(category): 916 if category.__class__ == LetterMenuItem: 917 return False 918 return True 919 920 return category_filter 921 922 # def get_game_filter(self): 923 # def game_filter(game_info): 924 # if game_info.sort_title[0] == self.letter: 925 # return True 926 # if self.letter == "#": 927 # if game_info.sort_title[0] in "0123456789": 928 # return True 929 # return False 930 # return game_filter 931 932 def get_filter_clause(self): 933 # FIXME: NUMBERS 934 # if self.letter == "#": 935 # clause = ["AND (0 = 1"] 936 # params = [] 937 # for c in "0123456789": 938 # clause.append(" OR name LIKE ?") 939 # params.append("{0}%".format(c)) 940 # clause.append(")") 941 # return "".join(clause), params 942 # return "AND name LIKE ?", ["{0}%".format(self.letter)] 943 return "l:{0}".format(self.letter.lower()), [] 944 945 946class YearMenuItem(MenuItem): 947 def __init__(self): 948 MenuItem.__init__(self) 949 self.title = gettext("Year") 950 self.path_title_active = "Choose Year" 951 952 def activate(self, menu): 953 print("YearMenuItem.activate") 954 menu = create_item_menu(gettext("Select Year")) 955 # for year in GameList().get().get_years(): # FIXME: GET YEARS FROM 956 # FILTERED GAME LIST 957 # item = YearItem(year) 958 # menu.append(item) 959 return menu 960 961 962class YearItem(AutoExpandItem): 963 def __init__(self, year): 964 AutoExpandItem.__init__(self) 965 self.year = year 966 self.title = str(year) 967 self.path_title = str(year) 968 969 def get_category_filter(self): 970 def category_filter(category): 971 if category.__class__ == YearMenuItem: 972 return False 973 return True 974 975 return category_filter 976 977 # def get_game_filter(self): 978 # def game_filter(game_info): 979 # if game_info.year == self.year: 980 # return True 981 # return False 982 # return game_filter 983 984 985class KeywordMenuItem(MenuItem): 986 def __init__(self): 987 MenuItem.__init__(self) 988 self.title = gettext("Keyword") 989 self.path_title_active = "Choose Keyword" 990 991 def activate(self, menu): 992 print("KeywordMenuItem.activate") 993 new_menu = create_item_menu(gettext("Select Keyword")) 994 menu_path = self.create_menu_path(menu) 995 new_menu.update_path(menu_path) 996 category_filters = list(self.generate_category_filters(menu_path)) 997 998 def check(cat): 999 for flt in category_filters: 1000 if not flt(cat): 1001 return False 1002 return True 1003 1004 # for keyword in GameList().get().get_keywords(): 1005 # item = KeywordItem(keyword) 1006 # if check(item): 1007 # new_menu.append(item) 1008 return new_menu 1009 1010 1011class KeywordItem(AutoExpandItem): 1012 def __init__(self, keyword): 1013 AutoExpandItem.__init__(self) 1014 self.keyword = keyword 1015 self.title = keyword 1016 self.path_title = keyword 1017 1018 def get_category_filter(self): 1019 def category_filter(category): 1020 if category.__class__ == KeywordItem: 1021 if category.keyword == self.keyword: 1022 return False 1023 return True 1024 1025 return category_filter 1026 1027 # def get_game_filter(self): 1028 # def game_filter(game_info): 1029 # if self.keyword in game_info.keywords: 1030 # return True 1031 # return False 1032 # return game_filter 1033 1034 1035class GameItem(MenuItem): 1036 def __init__(self, game_info): 1037 MenuItem.__init__(self) 1038 self.game_info = game_info 1039 # self.id = id 1040 self.uuid = game_info[0] 1041 self.title = game_info[1] 1042 1043 # id, name, configurations, platform, ratio=0.75, 1044 # screenratio=1.33, year="", publisher="", developer="", 1045 # subtitlepos=-1 1046 1047 # when a game item is the selected item in the parent menu, 1048 # we are displaying category items 1049 self.path_title_active = "Add Filter" 1050 1051 # self.name = name 1052 self.configurations = [] 1053 # self.platform = platform 1054 # print("self.platform = ", self.platform) 1055 # self.title = name 1056 # FIXME: Get correct year 1057 # self.year = year 1058 # self.publisher = publisher 1059 # self.developer = developer 1060 self.subtitle = "" 1061 1062 ratio = 0.75 1063 screenratio = 1.33 1064 1065 if ratio: 1066 self.ratio = ratio 1067 else: 1068 # if self.platform == "Game Boy": 1069 # self.ratio = 1.0 1070 # elif self.platform == "Game Boy Color": 1071 # self.ratio = 1.0 1072 # elif self.platform == "Game Boy Advance": 1073 # self.ratio = 1.0 1074 # elif self.platform == "Super Nintendo": 1075 # self.ratio = 1.33 1076 # elif self.platform == "Nintendo 64": 1077 # self.ratio = 1.33 1078 # else: 1079 # self.ratio = 0.75 1080 self.ratio = 0.75 1081 self.screenratio = screenratio or 1.33 1082 1083 # FIXME: REMOVE 1084 self.ratio = 0.75 1085 1086 # if subtitlepos > 0: 1087 # self.title = name[:subtitlepos].strip() 1088 # self.subtitle = name[subtitlepos:].strip() 1089 # elif subtitlepos < 0: 1090 # self.supertitle = name[:-subtitlepos].strip() 1091 # self.title = name[-subtitlepos:].strip() 1092 # else: 1093 # self.fix_titles() 1094 1095 @property 1096 def name(self): 1097 return self.game_info[1] 1098 1099 # @property 1100 # def title(self): 1101 # return self.game_info[1] 1102 1103 @property 1104 def platform(self): 1105 return self.game_info[2] 1106 1107 @property 1108 def year(self): 1109 return self.game_info[3] 1110 1111 @property 1112 def publisher(self): 1113 return self.game_info[4] 1114 1115 def fix_titles(self): 1116 try: 1117 title, subtitle = self.title.split(":", 1) 1118 except ValueError: 1119 pass 1120 else: 1121 # if len(title) >= 8: 1122 self.title = title 1123 self.subtitle = subtitle 1124 if not self.subtitle: 1125 if "(" in self.title: 1126 if self.title[-1] == ")": 1127 pos = self.title.index("(") 1128 self.subtitle = self.title[pos + 1 : -1].strip() 1129 self.title = self.title[:pos].strip() 1130 # if not self.subtitle: 1131 # if len(self.title) > 30 and " in " in self.title: 1132 # pos = self.title.find(" in ") 1133 # if pos > 8: 1134 # self.subtitle = self.title[pos+4:].strip() 1135 # self.title = self.title[:pos+4].strip() 1136 # self.subtitle = self.subtitle[0].upper() + 1137 # self.subtitle[1:] 1138 if not self.subtitle: 1139 try: 1140 title, subtitle = self.title.split(" - ", 1) 1141 except ValueError: 1142 pass 1143 else: 1144 if len(title) >= 8: 1145 self.title = title 1146 self.subtitle = subtitle 1147 if not self.subtitle: 1148 pos = self.title.find(" and the ") 1149 if pos > 7: 1150 self.subtitle = self.title[pos:].strip() 1151 self.title = self.title[:pos].strip() 1152 # self.subtitle = self.subtitle[0].upper() + self.subtitle[1:] 1153 1154 def activate(self, menu): 1155 try: 1156 from arcade.glui.gamemenu import GameMenu 1157 1158 new_menu = GameMenu(self) 1159 except Exception: 1160 show_exception() 1161 print("returning None") 1162 return None 1163 return new_menu 1164 1165 def get_default_texture(self): 1166 return Texture.missing_cover 1167 1168 @memoize 1169 def get_image_path(self): 1170 cmp_name = GameNameUtil.create_cmpname(self.name) 1171 1172 # FIXME: FIXME: DISABLED TEMPORARILY 1173 # FIXME: USING 512-size only for now 1174 1175 # if False and os.path.exists("../fs-game-database/game/texture"): 1176 # if os.path.exists("../fs-game-database/game/texture"): 1177 # base_path = "../fs-game-database/game/texture" 1178 # path = os.path.join(base_path, NameUtil.create_cmpname( 1179 # self.platform), cmp_name, "front-512.jpg") 1180 # #self.platform), cmp_name, "front-1024.jpg") 1181 # if os.path.exists(path): 1182 # return path 1183 # #else: 1184 # base_path = os.path.join(pyapp.app.get_data_dir(), "texture") 1185 # if os.path.exists("c:\\data\\game\\info"): 1186 # base_path = "c:\\data\\game\\info" 1187 # else: 1188 # base_path = os.path.join(pyapp.app.get_data_dir(), "info") 1189 # base_path = os.path.join(GameCenter.data_dir, "info") 1190 # path = os.path.join(base_path, NameUtil.create_cmpname(self.platform), 1191 # cmp_name, "front.jpg") 1192 # if not os.path.exists(path): 1193 # base_path = "c:\\data\\game\\info" 1194 # path = os.path.join(base_path, NameUtil.create_cmpname( 1195 # self.platform), cmp_name, "front.jpg") 1196 # #cmp_name, "front-1024.jpg") 1197 # if not os.path.exists(path): 1198 # path = os.path.join(base_path, 1199 # NameUtil.create_cmpname(self.platform), 1200 # cmp_name, "front-512.jpg") 1201 # path = os.path.join(NameUtil.create_cmpname(self.platform), 1202 # cmp_name, "front.jpg") 1203 # return path 1204 # return self.game_info[5] + "?s=512&t=jpg" 1205 if not self.game_info[5]: 1206 return None 1207 return self.game_info[5] + "?w=480&h=640&t=cc&f=jpg" 1208 1209 # @memoize 1210 # def get_image_files(self): 1211 # path = self.get_image_path() 1212 # if os.path.exists(path): 1213 # return [path] 1214 # return [] 1215 1216 @memoize 1217 def get_image_files(self): 1218 path = self.get_image_path() 1219 # if path and os.path.exists(path): 1220 if path: 1221 return [path] 1222 return [] 1223 1224 @memoize 1225 def get_screen_path(self, n): 1226 # cmp_name = NameUtil.create_cmpname(self.name) 1227 # base_path = os.path.join(GameCenter.data_dir, "info") 1228 # path = os.path.join(base_path, NameUtil.create_cmpname(self.platform), 1229 # cmp_name, "screen{0}.png".format(n)) 1230 # if not os.path.exists(path): 1231 # base_path = "c:\\data\\game\\info" 1232 # path = os.path.join(base_path, NameUtil.create_cmpname( 1233 # self.platform), cmp_name, "screen{0}.png".format(n)) 1234 # if not os.path.exists(path): 1235 # return None 1236 1237 assert 1 <= n <= 5 1238 value = self.game_info[7 + n - 1] 1239 # if value: 1240 # value = "sha1:" + value 1241 1242 # """ 1243 # if n == 1: 1244 # name = "screen1.png" 1245 # elif n == 2: 1246 # name = "title.png" 1247 # else: 1248 # name = "screen{0}.png".format(n - 1) 1249 # """ 1250 # name = "screen{0}.png".format(n) 1251 # path = os.path.join(NameUtil.create_cmpname( 1252 # self.platform), cmp_name, name) 1253 if not value: 1254 return None 1255 return value + "?s=1x" 1256 1257 def get_screen_texture(self, n): 1258 path = self.get_screen_path(n) 1259 # print("get_screen_texture", repr(path)) 1260 1261 if path: 1262 # FIXME: LOAD WITH THE REST OF THE COVERS! 1263 TextureManager.get().load_images([path]) 1264 try: 1265 texture = TextureManager.get().get_texture(path) 1266 except KeyError: 1267 texture = None 1268 if not texture: 1269 Render.get().dirty = DIRTY_WHILE_NOT_LOADED 1270 if texture is Texture.default_item: 1271 return Texture.missing_screenshot 1272 return texture 1273 return Texture.missing_screenshot 1274