Merge pull request #3 from KevinCathcart/pyinstaller
Windows Bundle and Installer
This commit is contained in:
commit
e7f7ebd2d1
|
@ -4,3 +4,6 @@ build
|
|||
.idea
|
||||
*.sfc
|
||||
*_Spoiler.txt
|
||||
bundle/components.wxs
|
||||
*.wixobj
|
||||
README.html
|
||||
|
|
|
@ -7,6 +7,7 @@ import sys
|
|||
|
||||
from Main import main
|
||||
from Gui import guiMain
|
||||
from Utils import is_bundled, close_console
|
||||
|
||||
|
||||
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
|
||||
|
@ -190,6 +191,15 @@ if __name__ == '__main__':
|
|||
''')
|
||||
args = parser.parse_args()
|
||||
|
||||
if is_bundled and len(sys.argv) == 1 :
|
||||
# for the bundled builds, if we have no arguments, the user
|
||||
# probably wants the gui. Users of the bundled build who want the command line
|
||||
# interface shouuld specify at least one option, possibly setting a value to a
|
||||
# default if they like all the defaults
|
||||
close_console()
|
||||
guiMain()
|
||||
sys.exit(0)
|
||||
|
||||
# ToDo: Validate files further than mere existance
|
||||
if not args.jsonout and not os.path.isfile(args.rom):
|
||||
input('Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom)
|
||||
|
|
33
Gui.py
33
Gui.py
|
@ -1,14 +1,19 @@
|
|||
from Main import main, __version__ as ESVersion
|
||||
from Utils import is_bundled, local_path, output_path, open_file
|
||||
from argparse import Namespace
|
||||
import random
|
||||
|
||||
from tkinter import Checkbutton, OptionMenu, Tk, LEFT, RIGHT, BOTTOM, TOP, StringVar, IntVar, Frame, Label, W, E, Entry, Spinbox, Button, filedialog, messagebox
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
from tkinter import Checkbutton, OptionMenu, Tk, LEFT, RIGHT, BOTTOM, TOP, StringVar, IntVar, Frame, Label, W, E, X, Entry, Spinbox, Button, filedialog, messagebox, PhotoImage
|
||||
|
||||
|
||||
def guiMain(args=None):
|
||||
mainWindow = Tk()
|
||||
mainWindow.wm_title("Entrance Shuffle %s" % ESVersion)
|
||||
|
||||
set_icon(mainWindow)
|
||||
|
||||
topFrame = Frame(mainWindow)
|
||||
rightHalfFrame = Frame(topFrame)
|
||||
checkBoxFrame = Frame(rightHalfFrame)
|
||||
|
@ -164,6 +169,7 @@ def guiMain(args=None):
|
|||
heartbeepFrame.pack(expand=True, anchor=E)
|
||||
|
||||
bottomFrame = Frame(mainWindow)
|
||||
farBottomFrame = Frame(mainWindow)
|
||||
|
||||
seedLabel = Label(bottomFrame, text='Seed #')
|
||||
seedVar = StringVar()
|
||||
|
@ -212,15 +218,29 @@ def guiMain(args=None):
|
|||
|
||||
generateButton = Button(bottomFrame, text='Generate Patched Rom', command=generateRom)
|
||||
|
||||
def open_output():
|
||||
open_file(output_path(''))
|
||||
|
||||
openOutputButton = Button(farBottomFrame, text='Open Output Directory', command=open_output)
|
||||
|
||||
if os.path.exists(local_path('README.html')):
|
||||
def open_readme():
|
||||
open_file(local_path('README.html'))
|
||||
openReadmeButton = Button(farBottomFrame, text='Open Documentation', command=open_readme)
|
||||
openReadmeButton.pack(side=LEFT)
|
||||
|
||||
seedLabel.pack(side=LEFT)
|
||||
seedEntry.pack(side=LEFT)
|
||||
countLabel.pack(side=LEFT)
|
||||
countLabel.pack(side=LEFT, padx=(5,0))
|
||||
countSpinbox.pack(side=LEFT)
|
||||
generateButton.pack(side=LEFT)
|
||||
generateButton.pack(side=LEFT, padx=(5,0))
|
||||
|
||||
openOutputButton.pack(side=RIGHT)
|
||||
|
||||
drowDownFrame.pack(side=LEFT)
|
||||
rightHalfFrame.pack(side=RIGHT)
|
||||
topFrame.pack(side=TOP)
|
||||
farBottomFrame.pack(side=BOTTOM, fill=X, padx=5, pady=5)
|
||||
bottomFrame.pack(side=BOTTOM)
|
||||
|
||||
if args is not None:
|
||||
|
@ -254,6 +274,11 @@ def guiMain(args=None):
|
|||
|
||||
mainWindow.mainloop()
|
||||
|
||||
def set_icon(window):
|
||||
er16 = PhotoImage(file=local_path('data/ER16.gif'))
|
||||
er32 = PhotoImage(file=local_path('data/ER32.gif'))
|
||||
er48 = PhotoImage(file=local_path('data/ER32.gif'))
|
||||
window.tk.call('wm', 'iconphoto', window._w, er16, er32, er48)
|
||||
|
||||
if __name__ == '__main__':
|
||||
guiMain()
|
||||
|
|
5
Main.py
5
Main.py
|
@ -8,6 +8,7 @@ from Items import ItemFactory
|
|||
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, fill_restrictive, flood_items
|
||||
from collections import OrderedDict
|
||||
from ItemList import generate_itempool
|
||||
from Utils import output_path
|
||||
import random
|
||||
import time
|
||||
import logging
|
||||
|
@ -105,10 +106,10 @@ def main(args, seed=None):
|
|||
if args.jsonout:
|
||||
print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()}))
|
||||
else:
|
||||
rom.write_to_file(args.jsonout or '%s.sfc' % outfilebase)
|
||||
rom.write_to_file(args.jsonout or output_path('%s.sfc' % outfilebase))
|
||||
|
||||
if args.create_spoiler and not args.jsonout:
|
||||
world.spoiler.to_file('%s_Spoiler.txt' % outfilebase)
|
||||
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
||||
|
||||
logger.info('Done. Enjoy.')
|
||||
logger.debug('Total Time: %s' % (time.clock() - start))
|
||||
|
|
234
README.md
234
README.md
|
@ -3,7 +3,7 @@
|
|||
This is a entrance randomizer for _The Legend of Zelda: A Link to the Past_ for the SNES.
|
||||
See http://vt.alttp.run for more details on the normal randomizer.
|
||||
|
||||
## Installation
|
||||
# Installation
|
||||
|
||||
Clone this repository and then run ```EntranceRandomizer.py``` (requires Python 3).
|
||||
|
||||
|
@ -11,40 +11,9 @@ Alternatively, run ```Gui.py``` for a simple graphical user interface.
|
|||
|
||||
For releases, a Windows standalone executable is available for users without Python 3.
|
||||
|
||||
## Options
|
||||
# Settings
|
||||
|
||||
|
||||
```
|
||||
-h, --help
|
||||
```
|
||||
|
||||
Show the help message and exit.
|
||||
|
||||
```
|
||||
--create_spoiler
|
||||
```
|
||||
|
||||
Output a Spoiler File (default: False)
|
||||
|
||||
```
|
||||
--logic [{noglitches,minorglitches}]
|
||||
```
|
||||
|
||||
Select Enforcement of Item Requirements.
|
||||
|
||||
### No Glitches
|
||||
|
||||
The game can be completed without knowing how to perform glitches of any kind.
|
||||
|
||||
### Minor Glitches
|
||||
|
||||
May require Fake Flippers, Bunny Revival. (default: noglitches)
|
||||
|
||||
```
|
||||
--mode [{standard,open,swordless}]
|
||||
```
|
||||
|
||||
Select game mode. (default: open)
|
||||
## Game Mode
|
||||
|
||||
### Standard
|
||||
|
||||
|
@ -74,13 +43,20 @@ Special notes:
|
|||
- The magic barrier to Hyrule Castle Tower can be broken with a Hammer.
|
||||
- The Hammer can be used to activate the Ether and Bombos tablets.
|
||||
|
||||
```
|
||||
--goal [{ganon,pedestal,dungeons,triforcehunt,crystals}]
|
||||
```
|
||||
## Game Logic
|
||||
This determines the Item Requirements for each location.
|
||||
|
||||
Select completion goal.
|
||||
### No Glitches
|
||||
|
||||
### Ganon (Default)
|
||||
The game can be completed without knowing how to perform glitches of any kind.
|
||||
|
||||
### Minor Glitches
|
||||
|
||||
May require Fake Flippers, Bunny Revival.
|
||||
|
||||
## Game Goal
|
||||
|
||||
### Ganon
|
||||
|
||||
Standard game completion requiring you to collect the 7 crystals, defeat Agahnim 2 and then beat Ganon.
|
||||
|
||||
|
@ -109,18 +85,14 @@ Standard game completion requiring you to collect the 7 crystals and then beat G
|
|||
|
||||
This is only noticeably different if the --shuffleganon option is enabled.
|
||||
|
||||
```
|
||||
--difficulty [{easy,normal,hard,expert,insane}]
|
||||
```
|
||||
|
||||
Select game difficulty. Affects available itempool. (default: normal)
|
||||
## Game Difficulty
|
||||
|
||||
### Easy
|
||||
|
||||
This setting doubles the number of swords, shields, armors, and bottles in the item pool.
|
||||
Within dungeons, the number of items found will be displayed on screen if there is no timer.
|
||||
|
||||
### Normal (Default)
|
||||
### Normal
|
||||
|
||||
This is the default setting that has an item pool most similar to the original
|
||||
The Legend of Zelda: A Link to the Past.
|
||||
|
@ -142,13 +114,9 @@ pool is less helpful, and the player can find no armor, only a Master Sword, and
|
|||
This setting is a modest step up from Expert. The main difference is that the player will never find any
|
||||
additional health.
|
||||
|
||||
```
|
||||
--timer [{none,display,timed,timed-ohko,ohko,timed-countdown}]
|
||||
```
|
||||
## Timer Setting
|
||||
|
||||
Select the timer setting.
|
||||
|
||||
### None (Default)
|
||||
### None
|
||||
|
||||
Does not invoke a timer.
|
||||
|
||||
|
@ -180,11 +148,9 @@ decreased with Red Clocks found in chests that will be added to the itempool. Th
|
|||
is to finish the game without the timer reaching zero, but the game will continue uninterrupted if
|
||||
the player runs out of time.
|
||||
|
||||
```
|
||||
--progressive [{on,off,random}]
|
||||
```
|
||||
## Progressive equipment
|
||||
|
||||
Select the setting for progressive equipment.
|
||||
Determines if Sword, Shield, and gloves are progressive (upgrading in sequence) or not.
|
||||
|
||||
### On (Default)
|
||||
|
||||
|
@ -204,14 +170,12 @@ will simply do nothing.
|
|||
This setting makes swords, shields, armor, and gloves randomly either progressive or not. Each category is independently
|
||||
randomized.
|
||||
|
||||
```
|
||||
--algorithm [{freshness,flood,vt21,vt22,vt25,vt26,balanced}]
|
||||
```
|
||||
## Item Distribution Algorithm
|
||||
|
||||
Select item filling algorithm.
|
||||
Determines how the items are shuffled.
|
||||
|
||||
### Balanced (Default)
|
||||
This is a variation of vt26 that aims to strike a balance between the overworld heavy vt25 and the dungeon heavy vt26 algorithm.
|
||||
### Balanced
|
||||
This is a variation of VT26 that aims to strike a balance between the overworld heavy VT25 and the dungeon heavy VT26 algorithm.
|
||||
It does this by reshuffling the remaining locations after placing dungeon items.
|
||||
|
||||
### VT26
|
||||
|
@ -223,14 +187,14 @@ the sheer number of chests in that single location.
|
|||
Items and locations are shuffled and placed from the top of the lists. The only thing preventing an item from being placed into a spot
|
||||
is if is absolutely impossible to be there given the previous made placement choices. Leads to very uniform but guaranteed solvable distributions.
|
||||
|
||||
### VT22
|
||||
The ordinary VT v8.22 algorithm. Fixes issues in placement in VT21 by discarding all previously skipped and unfilled locations
|
||||
after 2/3 of the progression items were placed to prevent stale late game locations from soaking up the same items all the time.
|
||||
|
||||
### VT21
|
||||
The ordinary VT v8.21 algorithm. Unbiased placement of items into unlocked locations, placing items that unlock new locations first.
|
||||
May lead to distributions that seem a bit wonky (high likelyhood of ice rod in Turtle Rock, for instance)
|
||||
|
||||
### VT22
|
||||
The ordinary VT v8.21 algorithm. Fixes issues in placement in VT21 by discarding all previously skipped and unfilled locations
|
||||
after 2/3 of the progression items were placed to prevent stale late game locations from soaking up the same items all the time.
|
||||
|
||||
### Flood
|
||||
Pushes out items starting from Link's House and is slightly biased to placing progression items with less restrictions. Use for relatively simple distributions.
|
||||
|
||||
|
@ -238,11 +202,9 @@ Pushes out items starting from Link's House and is slightly biased to placing pr
|
|||
Alternative approach to VT22 to improve on VT21 flaws. Locations that are skipped because they are currently unreachable increase in
|
||||
staleness, decreasing the likelihood of receiving a progress item.
|
||||
|
||||
```
|
||||
--shuffle [{default,simple,restricted,full,madness,insanity,dungeonsfull,dungeonssimple}]
|
||||
```
|
||||
## Entrance Shuffle Algorithm
|
||||
|
||||
Select Entrance Shuffling Algorithm.
|
||||
Determines how locations are shuffled.
|
||||
|
||||
### Default
|
||||
|
||||
|
@ -253,7 +215,7 @@ Is the Vanilla layout.
|
|||
Shuffles Dungeon Entrances/Exits between each other and keeps all 4-entrance dungeons confined to one location. Outside Light World Death Mountain, interiors are shuffled but still connect the same points
|
||||
on the overworld. On Death Mountain, entrances are connected more freely.
|
||||
|
||||
### Full (Default)
|
||||
### Full
|
||||
|
||||
Mixes cave and dungeon entrances freely.
|
||||
|
||||
|
@ -273,6 +235,118 @@ Madness, but without the light/dark world restrictions. Gives access to Mirror a
|
|||
|
||||
The dungeon variants only mix up dungeons and keep the rest of the overworld vanilla.
|
||||
|
||||
## Heartbeep Sound Rate
|
||||
|
||||
Select frequency of beeps when on low health. Can completely disable them.
|
||||
|
||||
## Create Spoiler Log
|
||||
|
||||
Output a Spoiler File.
|
||||
|
||||
## Do not Create Patched Rom
|
||||
|
||||
If set, will not produce a patched rom as output. Useful in conjunction with the spoiler log option to batch
|
||||
generate spoilers for statistical analysis.
|
||||
|
||||
## Enable L/R button quickswapping
|
||||
|
||||
Use to enable quick item swap with L/R buttons
|
||||
|
||||
## Instant Menu
|
||||
|
||||
As an alternative to quickswap, opens menu instantly.
|
||||
|
||||
## Keysanity
|
||||
|
||||
This setting allows dungeon specific items (Small Key, Big Key, Map, Compass) to be distributed anywhere in the world and not just
|
||||
in their native dungeon. Small Keys dropped by enemies or found in pots are not affected. The chest in southeast Skull Woods that
|
||||
is traditionally a guaranteed Small Key still is. These items will be distributed according to the v26/balanced algorithm, but
|
||||
the rest of the itempool will respect the algorithm setting. Music for dungeons is randomized so it cannot be used as a tell
|
||||
for which dungeons contain pendants and crystals; finding a Map for a dungeon will allow the overworld map to display its prize.
|
||||
|
||||
## Place Dungeon Items
|
||||
|
||||
If not set, Compasses and Maps are removed from the dungeon item pools and replaced by empty chests that may end up anywhere in the world.
|
||||
This may lead to different amount of itempool items being placed in a dungeon than you are used to.
|
||||
|
||||
## Only Ensure Seed Beatable
|
||||
|
||||
If set, will only ensure the goal can be achieved, but not necessarily that all locations are reachable. Currently only affects VT25, VT26 and balanced algorithms.
|
||||
|
||||
## Include Ganon's Tower and Pyramid Hole in Shuffle pool
|
||||
|
||||
If set, Ganon's Tower is included in the dungeon shuffle pool and the Pyramid Hole/Exit pair is included in the Holes shuffle pool. Ganon can not be defeated until the primary goal is fulfilled. This setting removes any bias against Ganon's Tower that some algorithms may have.
|
||||
|
||||
## Seed
|
||||
|
||||
Can be used to set a seed number to generate. Using the same seed with same settings on the same version of the entrance randomizer will always yield an identical output.
|
||||
|
||||
## Count
|
||||
|
||||
Use to batch generate multiple seeds with same settings. If a seed number is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with the same seed number given will produce the same 10 (different) roms each time).
|
||||
|
||||
# Command Line Options
|
||||
|
||||
```
|
||||
-h, --help
|
||||
```
|
||||
|
||||
Show the help message and exit.
|
||||
|
||||
```
|
||||
--create_spoiler
|
||||
```
|
||||
|
||||
Output a Spoiler File (default: False)
|
||||
|
||||
```
|
||||
--logic [{noglitches,minorglitches}]
|
||||
```
|
||||
|
||||
Select the game logic (default: noglitches)
|
||||
|
||||
```
|
||||
--mode [{standard,open,swordless}]
|
||||
```
|
||||
|
||||
Select the game mode. (default: open)
|
||||
|
||||
```
|
||||
--goal [{ganon,pedestal,dungeons,triforcehunt,crystals}]
|
||||
```
|
||||
|
||||
Select the game completion goal. (default: ganon)
|
||||
|
||||
```
|
||||
--difficulty [{easy,normal,hard,expert,insane}]
|
||||
```
|
||||
|
||||
Select the game difficulty. Affects available itempool. (default: normal)
|
||||
|
||||
```
|
||||
--timer [{none,display,timed,timed-ohko,ohko,timed-countdown}]
|
||||
```
|
||||
|
||||
Select the timer setting. (default: none)
|
||||
|
||||
```
|
||||
--progressive [{on,off,random}]
|
||||
```
|
||||
|
||||
Select the setting for progressive equipment. (default: on)
|
||||
|
||||
```
|
||||
--algorithm [{freshness,flood,vt21,vt22,vt25,vt26,balanced}]
|
||||
```
|
||||
|
||||
Select item distribution algorithm. (default: balanced)
|
||||
|
||||
```
|
||||
--shuffle [{default,simple,restricted,full,madness,insanity,dungeonsfull,dungeonssimple}]
|
||||
```
|
||||
|
||||
Select entrance shuffle algorithm. (default: full)
|
||||
|
||||
```
|
||||
--rom ROM
|
||||
```
|
||||
|
@ -289,14 +363,13 @@ Select level of logging for output. (default: info)
|
|||
--seed SEED
|
||||
```
|
||||
|
||||
Define seed number to generate. (default: None) Using the same seed with same settings on the same version of the entrance randomizer will always yield an identical output.
|
||||
Define seed number to generate. (default: None)
|
||||
|
||||
```
|
||||
--count COUNT
|
||||
```
|
||||
|
||||
Use to batch generate multiple seeds with same settings.
|
||||
If --seed is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with --seed given will produce the same 10 (different) roms each time). (default: None)
|
||||
Set the count option (default: None)
|
||||
|
||||
```
|
||||
--quickswap
|
||||
|
@ -321,11 +394,7 @@ Disables game music, resulting in the game sound being just the SFX. (default: F
|
|||
--keysanity
|
||||
```
|
||||
|
||||
This setting allows dungeon specific items (Small Key, Big Key, Map, Compass) to be distributed anywhere in the world and not just
|
||||
in their native dungeon. Small Keys dropped by enemies or found in pots are not affected. The chest in southeast Skull Woods that
|
||||
is traditionally a guaranteed Small Key still is. These items will be distributed according to the v26/balanced algorithm, but
|
||||
the rest of the itempool will respect the algorithm setting. Music for dungeons is randomized so it cannot be used as a tell
|
||||
for which dungeons contain pendants and crystals; finding a Map for a dungeon will allow the overworld map to display its prize.
|
||||
Enable Keysanity (default: False)
|
||||
|
||||
```
|
||||
--nodungeonitems
|
||||
|
@ -350,22 +419,19 @@ Use to select a different sprite sheet to use for Link. Path to a binary file of
|
|||
--beatableonly
|
||||
```
|
||||
|
||||
If set, will only ensure the goal can be achieved, but not necessarily that all locations are reachable. Currently only affects VT25, VT26 and balanced algorithms.
|
||||
Enables the "Only Ensure Seed Beatable" option (default: False)
|
||||
|
||||
```
|
||||
--shuffleganon
|
||||
```
|
||||
|
||||
If set, Ganon's Tower is included in the dungeon shuffle pool and the Pyramid Hole/Exit pair is included in the Holes shuffle pool. Ganon can not be defeated until the primary goal is fulfilled.
|
||||
This setting removes any bias against Ganon's Tower that some algorithms may have.
|
||||
|
||||
Note: This option is under development and may sometimes lead to dungeon and crystal distributions that cannot be solved. If this is the case, the generation will fail. Simply retry with a different seed number if you run into this issue.
|
||||
Enables the "Include Ganon's Tower and Pyramid Hole in Shuffle pool" option. (default: false)
|
||||
|
||||
```
|
||||
--suppress_rom
|
||||
```
|
||||
|
||||
If set, will not produce a patched rom as output. Useful to batch generate spoilers for statistical analysis.
|
||||
Enables the "Do not Create Patched Rom" option. (default: False)
|
||||
|
||||
```
|
||||
--gui
|
||||
|
|
3
Rom.py
3
Rom.py
|
@ -2,6 +2,7 @@ from Dungeons import dungeon_music_addresses
|
|||
from Text import string_to_alttp_text, text_addresses, Credits
|
||||
from Text import Uncle_texts, Ganon1_texts, PyramidFairy_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts
|
||||
from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts
|
||||
from Utils import local_path
|
||||
import random
|
||||
import json
|
||||
import hashlib
|
||||
|
@ -66,7 +67,7 @@ class LocalRom(object):
|
|||
self.buffer.extend(bytearray([0x00] * (2097152 - len(self.buffer))))
|
||||
|
||||
# load randomizer patches
|
||||
patches = json.load(open('data/base2current.json', 'r'))
|
||||
patches = json.load(open(local_path('data/base2current.json'), 'r'))
|
||||
for patch in patches:
|
||||
if isinstance(patch, dict):
|
||||
for baseaddress, values in patch.items():
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
def is_bundled():
|
||||
return getattr(sys, 'frozen', False)
|
||||
|
||||
def local_path(path):
|
||||
if local_path.cached_path is not None:
|
||||
return os.path.join(local_path.cached_path, path)
|
||||
|
||||
if is_bundled():
|
||||
# we are running in a bundle
|
||||
local_path.cached_path = sys._MEIPASS
|
||||
else:
|
||||
# we are running in a normal Python environment
|
||||
local_path.cached_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
return os.path.join(local_path.cached_path, path)
|
||||
|
||||
local_path.cached_path = None
|
||||
|
||||
def output_path(path):
|
||||
if output_path.cached_path is not None:
|
||||
return os.path.join(output_path.cached_path, path)
|
||||
|
||||
if not is_bundled():
|
||||
output_path.cached_path = '.'
|
||||
return os.path.join(output_path.cached_path, path)
|
||||
else:
|
||||
# has been packaged, so cannot use CWD for output.
|
||||
if sys.platform == 'win32':
|
||||
#windows
|
||||
import ctypes.wintypes
|
||||
CSIDL_PERSONAL = 5 # My Documents
|
||||
SHGFP_TYPE_CURRENT = 0 # Get current, not default value
|
||||
|
||||
buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
|
||||
ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf)
|
||||
|
||||
documents = buf.value
|
||||
|
||||
elif sys.platform == 'darwin':
|
||||
from AppKit import NSSearchPathForDirectoriesInDomains
|
||||
# http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains
|
||||
NSDocumentDirectory = 9
|
||||
NSUserDomainMask = 1
|
||||
# True for expanding the tilde into a fully qualified path
|
||||
documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0]
|
||||
else:
|
||||
raise NotImplementedError('Not supported yet')
|
||||
|
||||
output_path.cached_path = os.path.join(documents, 'ALttPEntranceRandomizer')
|
||||
if not os.path.exists(output_path.cached_path):
|
||||
os.mkdir(output_path.cached_path)
|
||||
return os.path.join(output_path.cached_path, path)
|
||||
|
||||
output_path.cached_path = None
|
||||
|
||||
def open_file(filename):
|
||||
if sys.platform == 'win32':
|
||||
os.startfile(filename)
|
||||
else:
|
||||
open_Command = 'open' if sys.platform == 'darwin' else 'xdg-open'
|
||||
subprocess.call([open_command, filename])
|
||||
|
||||
def close_console():
|
||||
if sys.platform == 'win32':
|
||||
#windows
|
||||
import ctypes.wintypes
|
||||
try:
|
||||
ctypes.windll.kernel32.FreeConsole()
|
||||
except:
|
||||
pass
|
|
@ -0,0 +1,28 @@
|
|||
version: '{build}'
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
environment:
|
||||
ProjectVersion: build$(APPVEYOR_BUILD_VERSION)
|
||||
matrix:
|
||||
- PYTHON: C:\PYTHON36
|
||||
install:
|
||||
- ps: 'if(Test-Path env:APPVEYOR_REPO_TAG_NAME) {$env:ProjectVersion=$env:APPVEYOR_REPO_TAG_NAME}'
|
||||
- '%PYTHON%\python.exe --version'
|
||||
- '%PYTHON%\Scripts\pip install pyinstaller'
|
||||
- '%PYTHON%\Scripts\pip install markdown'
|
||||
- '%PYTHON%\python.exe -m markdown README.md > README.html'
|
||||
- '%PYTHON%\Scripts\pyinstaller bundle\EntranceRandomizer.spec'
|
||||
- 'mkdir dist\EntranceRandomizer\ext'
|
||||
- 'move dist\EntranceRandomizer\*.pyd dist\EntranceRandomizer\ext'
|
||||
- 'move dist\EntranceRandomizer\tcl*.dll dist\EntranceRandomizer\ext'
|
||||
- 'move dist\EntranceRandomizer\tk*.dll dist\EntranceRandomizer\ext'
|
||||
- ps: '$env:ER_Version= &"$env:PYTHON\python.exe" -c "import Main; import re; print(re.match(''[0-9]+\\.[0-9]+\\.[0-9]+'',Main.__version__).group(0))"'
|
||||
- '"%WIX%\bin\heat.exe" dir "dist\EntranceRandomizer" -sfrag -srd -suid -dr INSTALLDIR -cg ERFiles -ag -template fragment -t bundle\components.xslt -out build\components.wxs'
|
||||
- '"%WIX%\bin\candle.exe" -out build\ bundle\*.wxs build\*.wxs'
|
||||
- '"%WIX%\bin\light.exe" -ext WixUIExtension build\*.wixobj -o dist\EntranceRandomizer-Installer-%ProjectVersion%-win32.msi -b dist\EntranceRandomizer'
|
||||
build: off
|
||||
artifacts:
|
||||
- path: dist/EntranceRandomizer*.msi
|
||||
name: EntranceRandomizer-Installer-$(ProjectVersion)-win32.msi
|
||||
- path: dist/EntranceRandomizer/
|
||||
name: EntranceRandomizer-Raw-$(ProjectVersion)-win32.zip
|
|
@ -0,0 +1,39 @@
|
|||
# -*- mode: python -*-
|
||||
from PyInstaller.compat import is_win
|
||||
block_cipher = None
|
||||
|
||||
# Todo: the runtime hooks should only be installed on windows
|
||||
a = Analysis(['../EntranceRandomizer.py'],
|
||||
pathex=['bundle'],
|
||||
binaries=[],
|
||||
datas=[('../data/', 'data/'), ('../README.html', '.')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=['bundle/_rt_hook.py'],
|
||||
excludes=['lzma', 'bz2'],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
exclude_binaries=True,
|
||||
name='EntranceRandomizer',
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=False,
|
||||
icon='data/ER.ico',
|
||||
console=is_win )
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=False,
|
||||
name='EntranceRandomizer')
|
||||
app = BUNDLE(coll,
|
||||
name ='EntranceRandomizer.app',
|
||||
icon = 'data/ER.icns',
|
||||
bundle_identifier = None)
|
|
@ -0,0 +1,4 @@
|
|||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.join(sys._MEIPASS, "ext"))
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
|
||||
exclude-result-prefixes="msxsl"
|
||||
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
|
||||
|
||||
<xsl:output method="xml" indent="no"/>
|
||||
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="wix:File[@Source='SourceDir\EntranceRandomizer.exe']">
|
||||
<xsl:copy-of select="." />
|
||||
<wix:Shortcut Id="ProgramShortcut"
|
||||
Name="ALttP Entrance Randomizer"
|
||||
Advertise="yes"
|
||||
Description="ALttP Entrance Randomizer"
|
||||
Directory="ApplicationProgramsFolder" />
|
||||
</xsl:template>
|
||||
<xsl:template match="wix:File[@Source='SourceDir\README.hmtl']">
|
||||
<xsl:copy-of select="." />
|
||||
<wix:Shortcut Id="ReadmeShortcut"
|
||||
Name="ALttP Entrance Randomizer README"
|
||||
Advertise="yes"
|
||||
Description="ALttP Entrance Randomizer README"
|
||||
Directory="ApplicationProgramsFolder" />
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<!-- Setting the product ID to "*" means all upgrades are treated as major upgrades, and will uninstall the old version
|
||||
before installing the new one. This is desireable because it means nothing breaks if we fail to follow the component rules precisely. -->
|
||||
<Product Id="*" Name="ALttP Entrance Randomizer" Language="1033" Version="$(env.ER_Version)" Manufacturer="Randomizer Community" UpgradeCode="0229C621-5F8A-4D59-962A-5826C58B93DD" >
|
||||
<Package Id="*" InstallerVersion="400" Compressed="yes" InstallScope="perMachine" />
|
||||
<!-- Allowing downgrades will cause a harmless warning to be emitted. This wearning is not relevant here, allowing downgrades via a new major upgrade is safe for a simple standalone app like this-->
|
||||
<MajorUpgrade AllowDowngrades="yes"/>
|
||||
<Media Id="1" Cabinet="contents.cab" EmbedCab="yes" CompressionLevel="high"/>
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id='ProgramFilesFolder' Name='PFiles'>
|
||||
<Directory Id='INSTALLDIR' Name='ALttP Entrance Randomizer'/>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="ApplicationProgramsFolder" Name="ALttP Entrance Randomizer"/>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||
<Component Id="ApplicationShortcut" Guid="0054698A-5A56-4B36-8176-8FEC1762EF2D">
|
||||
<RemoveFolder Id="CleanUpShortCut" Directory="ApplicationProgramsFolder" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU" Key="Software\ALttPEntranceRandomizer" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<Feature Id="Complete"
|
||||
Title="ALttP Entrance Randomizer"
|
||||
Description="ALttP Entrance Randomizer"
|
||||
Level="1">
|
||||
<ComponentGroupRef Id="ERFiles"/>
|
||||
<ComponentRef Id="ApplicationShortcut"/>
|
||||
</Feature>
|
||||
|
||||
<Icon Id="ER.ico" SourceFile="Data/ER.ico" />
|
||||
<Property Id="DISABLEADVTSHORTCUTS" Secure="yes">1</Property>
|
||||
<Property Id="ARPPRODUCTICON" Value="ER.ico" />
|
||||
<Property Id="WIXUI_INSTALLDIR">INSTALLDIR</Property>
|
||||
<UI>
|
||||
<UIRef Id="WixUI_InstallDir" />
|
||||
<UIRef Id="WixUI_ErrorProgressText" />
|
||||
<!-- Skip license page -->
|
||||
<Publish Dialog="WelcomeDlg"
|
||||
Control="Next"
|
||||
Event="NewDialog"
|
||||
Value="InstallDirDlg"
|
||||
Order="2">1</Publish>
|
||||
<Publish Dialog="InstallDirDlg"
|
||||
Control="Back"
|
||||
Event="NewDialog"
|
||||
Value="WelcomeDlg"
|
||||
Order="2">1</Publish>
|
||||
</UI>
|
||||
</Product>
|
||||
</Wix>
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
After Width: | Height: | Size: 123 B |
Binary file not shown.
After Width: | Height: | Size: 370 B |
Binary file not shown.
After Width: | Height: | Size: 882 B |
Loading…
Reference in New Issue