AP Ocarina of Time Client (#352)
This commit is contained in:
parent
3c2933d587
commit
469dda7d85
|
@ -0,0 +1,287 @@
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import multiprocessing
|
||||||
|
import subprocess
|
||||||
|
from asyncio import StreamReader, StreamWriter
|
||||||
|
|
||||||
|
from CommonClient import CommonContext, server_loop, gui_enabled, console_loop, \
|
||||||
|
ClientCommandProcessor, logger, get_base_parser
|
||||||
|
import Utils
|
||||||
|
from worlds import network_data_package
|
||||||
|
from worlds.oot.Rom import Rom, compress_rom_file
|
||||||
|
from worlds.oot.N64Patch import apply_patch_file
|
||||||
|
from worlds.oot.Utils import data_path
|
||||||
|
|
||||||
|
|
||||||
|
CONNECTION_TIMING_OUT_STATUS = "Connection timing out. Please restart your emulator, then restart oot_connector.lua"
|
||||||
|
CONNECTION_REFUSED_STATUS = "Connection refused. Please start your emulator and make sure oot_connector.lua is running"
|
||||||
|
CONNECTION_RESET_STATUS = "Connection was reset. Please restart your emulator, then restart oot_connector.lua"
|
||||||
|
CONNECTION_TENTATIVE_STATUS = "Initial Connection Made"
|
||||||
|
CONNECTION_CONNECTED_STATUS = "Connected"
|
||||||
|
CONNECTION_INITIAL_STATUS = "Connection has not been initiated"
|
||||||
|
|
||||||
|
"""
|
||||||
|
Payload: lua -> client
|
||||||
|
{
|
||||||
|
playerName: string,
|
||||||
|
locations: dict,
|
||||||
|
deathlinkActive: bool,
|
||||||
|
isDead: bool,
|
||||||
|
gameComplete: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
Payload: client -> lua
|
||||||
|
{
|
||||||
|
items: list,
|
||||||
|
playerNames: list,
|
||||||
|
triggerDeath: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
Deathlink logic:
|
||||||
|
"Dead" is true <-> Link is at 0 hp.
|
||||||
|
|
||||||
|
deathlink_pending: we need to kill the player
|
||||||
|
deathlink_sent_this_death: we interacted with the multiworld on this death, waiting to reset with living link
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
oot_loc_name_to_id = network_data_package["games"]["Ocarina of Time"]["location_name_to_id"]
|
||||||
|
|
||||||
|
def get_item_value(ap_id):
|
||||||
|
return ap_id - 66000
|
||||||
|
|
||||||
|
class OoTCommandProcessor(ClientCommandProcessor):
|
||||||
|
def __init__(self, ctx):
|
||||||
|
super().__init__(ctx)
|
||||||
|
|
||||||
|
def _cmd_n64(self):
|
||||||
|
"""Check N64 Connection State"""
|
||||||
|
if isinstance(self.ctx, OoTContext):
|
||||||
|
logger.info(f"N64 Status: {self.ctx.n64_status}")
|
||||||
|
|
||||||
|
|
||||||
|
class OoTContext(CommonContext):
|
||||||
|
command_processor = OoTCommandProcessor
|
||||||
|
items_handling = 0b001 # full local
|
||||||
|
|
||||||
|
def __init__(self, server_address, password):
|
||||||
|
super().__init__(server_address, password)
|
||||||
|
self.game = 'Ocarina of Time'
|
||||||
|
self.n64_streams: (StreamReader, StreamWriter) = None
|
||||||
|
self.n64_sync_task = None
|
||||||
|
self.n64_status = CONNECTION_INITIAL_STATUS
|
||||||
|
self.awaiting_rom = False
|
||||||
|
self.location_table = {}
|
||||||
|
self.deathlink_enabled = False
|
||||||
|
self.deathlink_pending = False
|
||||||
|
self.deathlink_sent_this_death = False
|
||||||
|
|
||||||
|
async def server_auth(self, password_requested: bool = False):
|
||||||
|
if password_requested and not self.password:
|
||||||
|
await super(OoTContext, self).server_auth(password_requested)
|
||||||
|
if not self.auth:
|
||||||
|
self.awaiting_rom = True
|
||||||
|
logger.info('Awaiting connection to Bizhawk to get player information')
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.send_connect()
|
||||||
|
|
||||||
|
def on_deathlink(self, data: dict):
|
||||||
|
self.deathlink_pending = True
|
||||||
|
super().on_deathlink(data)
|
||||||
|
|
||||||
|
|
||||||
|
def get_payload(ctx: OoTContext):
|
||||||
|
if ctx.deathlink_enabled and ctx.deathlink_pending:
|
||||||
|
trigger_death = True
|
||||||
|
ctx.deathlink_sent_this_death = True
|
||||||
|
else:
|
||||||
|
trigger_death = False
|
||||||
|
|
||||||
|
return json.dumps({
|
||||||
|
"items": [get_item_value(item.item) for item in ctx.items_received],
|
||||||
|
"playerNames": [name for (i, name) in ctx.player_names.items() if i != 0],
|
||||||
|
"triggerDeath": trigger_death
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
async def parse_payload(payload: dict, ctx: OoTContext, force: bool):
|
||||||
|
|
||||||
|
# Turn on deathlink if it is on
|
||||||
|
if payload['deathlinkActive'] and not ctx.deathlink_enabled:
|
||||||
|
await ctx.update_death_link(True)
|
||||||
|
ctx.deathlink_enabled = True
|
||||||
|
|
||||||
|
# Game completion handling
|
||||||
|
if payload['gameComplete'] and not ctx.finished_game:
|
||||||
|
await ctx.send_msgs([{
|
||||||
|
"cmd": "StatusUpdate",
|
||||||
|
"status": 30
|
||||||
|
}])
|
||||||
|
ctx.finished_game = True
|
||||||
|
|
||||||
|
# Locations handling
|
||||||
|
if ctx.location_table != payload['locations']:
|
||||||
|
ctx.location_table = payload['locations']
|
||||||
|
await ctx.send_msgs([{
|
||||||
|
"cmd": "LocationChecks",
|
||||||
|
"locations": [oot_loc_name_to_id[loc] for loc in ctx.location_table if ctx.location_table[loc]]
|
||||||
|
}])
|
||||||
|
|
||||||
|
# Deathlink handling
|
||||||
|
if ctx.deathlink_enabled:
|
||||||
|
if payload['isDead']: # link is dead
|
||||||
|
ctx.deathlink_pending = False
|
||||||
|
if not ctx.deathlink_sent_this_death:
|
||||||
|
ctx.deathlink_sent_this_death = True
|
||||||
|
await ctx.send_death()
|
||||||
|
else: # link is alive
|
||||||
|
ctx.deathlink_sent_this_death = False
|
||||||
|
|
||||||
|
|
||||||
|
async def n64_sync_task(ctx: OoTContext):
|
||||||
|
logger.info("Starting n64 connector. Use /n64 for status information.")
|
||||||
|
while not ctx.exit_event.is_set():
|
||||||
|
error_status = None
|
||||||
|
if ctx.n64_streams:
|
||||||
|
(reader, writer) = ctx.n64_streams
|
||||||
|
msg = get_payload(ctx).encode()
|
||||||
|
writer.write(msg)
|
||||||
|
writer.write(b'\n')
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(writer.drain(), timeout=1.5)
|
||||||
|
try:
|
||||||
|
# Data will return a dict with up to five fields:
|
||||||
|
# 1. str: player name (always)
|
||||||
|
# 2. bool: deathlink active (always)
|
||||||
|
# 3. dict[str, bool]: checked locations
|
||||||
|
# 4. bool: whether Link is currently at 0 HP
|
||||||
|
# 5. bool: whether the game currently registers as complete
|
||||||
|
data = await asyncio.wait_for(reader.readline(), timeout=10)
|
||||||
|
data_decoded = json.loads(data.decode())
|
||||||
|
if ctx.game is not None and 'locations' in data_decoded:
|
||||||
|
# Not just a keep alive ping, parse
|
||||||
|
asyncio.create_task(parse_payload(data_decoded, ctx, False))
|
||||||
|
if not ctx.auth:
|
||||||
|
ctx.auth = data_decoded['playerName']
|
||||||
|
if ctx.awaiting_rom:
|
||||||
|
await ctx.server_auth(False)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.debug("Read Timed Out, Reconnecting")
|
||||||
|
error_status = CONNECTION_TIMING_OUT_STATUS
|
||||||
|
writer.close()
|
||||||
|
ctx.n64_streams = None
|
||||||
|
except ConnectionResetError as e:
|
||||||
|
logger.debug("Read failed due to Connection Lost, Reconnecting")
|
||||||
|
error_status = CONNECTION_RESET_STATUS
|
||||||
|
writer.close()
|
||||||
|
ctx.n64_streams = None
|
||||||
|
except TimeoutError:
|
||||||
|
logger.debug("Connection Timed Out, Reconnecting")
|
||||||
|
error_status = CONNECTION_TIMING_OUT_STATUS
|
||||||
|
writer.close()
|
||||||
|
ctx.n64_streams = None
|
||||||
|
except ConnectionResetError:
|
||||||
|
logger.debug("Connection Lost, Reconnecting")
|
||||||
|
error_status = CONNECTION_RESET_STATUS
|
||||||
|
writer.close()
|
||||||
|
ctx.n64_streams = None
|
||||||
|
if ctx.n64_status == CONNECTION_TENTATIVE_STATUS:
|
||||||
|
if not error_status:
|
||||||
|
logger.info("Successfully Connected to N64")
|
||||||
|
ctx.n64_status = CONNECTION_CONNECTED_STATUS
|
||||||
|
else:
|
||||||
|
ctx.n64_status = f"Was tentatively connected but error occured: {error_status}"
|
||||||
|
elif error_status:
|
||||||
|
ctx.n64_status = error_status
|
||||||
|
logger.info("Lost connection to N64 and attempting to reconnect. Use /n64 for status updates")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
logger.debug("Attempting to connect to N64")
|
||||||
|
ctx.n64_streams = await asyncio.wait_for(asyncio.open_connection("localhost", 28921), timeout=10)
|
||||||
|
ctx.n64_status = CONNECTION_TENTATIVE_STATUS
|
||||||
|
except TimeoutError:
|
||||||
|
logger.debug("Connection Timed Out, Trying Again")
|
||||||
|
ctx.n64_status = CONNECTION_TIMING_OUT_STATUS
|
||||||
|
continue
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
logger.debug("Connection Refused, Trying Again")
|
||||||
|
ctx.n64_status = CONNECTION_REFUSED_STATUS
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
async def run_game(romfile):
|
||||||
|
auto_start = Utils.get_options()["oot_options"].get("rom_start", True)
|
||||||
|
if auto_start is True:
|
||||||
|
import webbrowser
|
||||||
|
webbrowser.open(romfile)
|
||||||
|
elif os.path.isfile(auto_start):
|
||||||
|
subprocess.Popen([auto_start, romfile],
|
||||||
|
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
|
||||||
|
async def patch_and_run_game(apz5_file):
|
||||||
|
base_name = os.path.splitext(apz5_file)[0]
|
||||||
|
decomp_path = base_name + '-decomp.z64'
|
||||||
|
comp_path = base_name + '.z64'
|
||||||
|
# Load vanilla ROM, patch file, compress ROM
|
||||||
|
rom = Rom(Utils.get_options()["oot_options"]["rom_file"])
|
||||||
|
apply_patch_file(rom, apz5_file)
|
||||||
|
rom.write_to_file(decomp_path)
|
||||||
|
os.chdir(data_path("Compress"))
|
||||||
|
compress_rom_file(decomp_path, comp_path)
|
||||||
|
os.remove(decomp_path)
|
||||||
|
asyncio.create_task(run_game(comp_path))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
Utils.init_logging("OoTClient")
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
multiprocessing.freeze_support()
|
||||||
|
parser = get_base_parser()
|
||||||
|
parser.add_argument('apz5_file', default="", type=str, nargs="?",
|
||||||
|
help='Path to an APZ5 file')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.apz5_file:
|
||||||
|
logger.info("APZ5 file supplied, beginning patching process...")
|
||||||
|
asyncio.create_task(patch_and_run_game(args.apz5_file))
|
||||||
|
|
||||||
|
ctx = OoTContext(args.connect, args.password)
|
||||||
|
ctx.server_task = asyncio.create_task(server_loop(ctx), name="Server Loop")
|
||||||
|
if gui_enabled:
|
||||||
|
input_task = None
|
||||||
|
from kvui import OoTManager
|
||||||
|
ctx.ui = OoTManager(ctx)
|
||||||
|
ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI")
|
||||||
|
else:
|
||||||
|
input_task = asyncio.create_task(console_loop(ctx), name="Input")
|
||||||
|
ui_task = None
|
||||||
|
|
||||||
|
ctx.n64_sync_task = asyncio.create_task(n64_sync_task(ctx), name="N64 Sync")
|
||||||
|
|
||||||
|
await ctx.exit_event.wait()
|
||||||
|
ctx.server_address = None
|
||||||
|
|
||||||
|
await ctx.shutdown()
|
||||||
|
|
||||||
|
if ctx.n64_sync_task:
|
||||||
|
await ctx.n64_sync_task
|
||||||
|
|
||||||
|
if ui_task:
|
||||||
|
await ui_task
|
||||||
|
|
||||||
|
if input_task:
|
||||||
|
input_task.cancel()
|
||||||
|
|
||||||
|
import colorama
|
||||||
|
|
||||||
|
colorama.init()
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
loop.close()
|
||||||
|
colorama.deinit()
|
|
@ -1,27 +1,40 @@
|
||||||
# Setup Guide for Ocarina of time Archipelago
|
# Setup Guide for Ocarina of Time Archipelago
|
||||||
|
|
||||||
## Important
|
## Important
|
||||||
|
|
||||||
As we are using Z5Client and BizHawk, this guide is only applicable to Windows.
|
As we are using Bizhawk, this guide is only applicable to Windows and Linux systems.
|
||||||
|
|
||||||
## Required Software
|
## Required Software
|
||||||
|
|
||||||
- BizHawk and Z5Client from: [Z5Client Releases Page](https://github.com/ArchipelagoMW/Z5Client/releases)
|
- Bizhawk: [Bizhawk Releases from TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory)
|
||||||
- We recommend download Z5Client-setup as it makes some steps automatic.
|
- Version 2.3.1 and later are supported. Version 2.7 is recommended for stability.
|
||||||
|
- Detailed installation instructions for Bizhawk can be found at the above link.
|
||||||
|
- Windows users must run the prereq installer first, which can also be found at the above link.
|
||||||
|
- An Archipelago client for Ocarina of Time. There are two options available:
|
||||||
|
- The built-in Archipelago client, which can be installed [here](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||||
|
(select `Ocarina of Time Client` during installation). This client is kept up-to-date with the latest Archipelago version
|
||||||
|
and will always be supported.
|
||||||
|
- Z5Client, which can be installed [here](https://github.com/ArchipelagoMW/Z5Client/releases), and its associated Lua script `ootMulti.lua`.
|
||||||
|
- An Ocarina of Time v1.0 ROM.
|
||||||
|
|
||||||
## Install Emulator and client
|
## Configuring Bizhawk
|
||||||
|
|
||||||
Download getBizhawk.ps1 from previous link. Place it on the folder where you want your emulator to be installed, right
|
Once Bizhawk has been installed, open Bizhawk and change the following settings:
|
||||||
click on it and select "Run with PowerShell". This will download all the needed dependencies used by the emulator. This
|
|
||||||
can take a while.
|
|
||||||
|
|
||||||
It is strongly recommended to associate N64 rom extension (\*.n64) to the BizHawk we've just installed. To do so, we
|
- Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to "Lua+LuaInterface".
|
||||||
simply have to search any N64 rom we happened to own, right click and select "Open with...", we unfold the list that
|
This is required for the Lua script to function correctly.
|
||||||
appears and select the bottom option "Look for another application", we browse to BizHawk folder and select EmuHawk.exe
|
- Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button. This reduces
|
||||||
|
the possibility of losing save data in emulator crashes.
|
||||||
|
- Under Config > Customize, check the "Run in background" and "Accept background input" boxes. This will allow you to continue
|
||||||
|
playing in the background, even if another window is selected.
|
||||||
|
- Under Config > Hotkeys, many hotkeys are listed, with many bound to common keys on the keyboard. You will likely want
|
||||||
|
to disable most of these, which you can do quickly using `Esc`.
|
||||||
|
- If playing with a controller, when you bind controls, disable "P1 A Up", "P1 A Down", "P1 A Left", and "P1 A Right" as these interfere
|
||||||
|
with aiming if bound. Set directional input using the Analog tab instead.
|
||||||
|
|
||||||
Place the ootMulti.lua file from the previous link inside the "lua" folder from the just installed emulator.
|
It is strongly recommended to associate N64 rom extensions (\*.n64, \*.z64) to the Bizhawk we've just installed. To do so, we
|
||||||
|
simply have to search any N64 rom we happened to own, right click and select "Open with...", unfold the list that
|
||||||
Install the Z5Client using its setup.
|
appears and select the bottom option "Look for another application", then browse to the Bizhawk folder and select EmuHawk.exe.
|
||||||
|
|
||||||
## Configuring your YAML file
|
## Configuring your YAML file
|
||||||
|
|
||||||
|
@ -33,7 +46,7 @@ an experience customized for their taste, and different players in the same mult
|
||||||
|
|
||||||
### Where do I get a YAML file?
|
### Where do I get a YAML file?
|
||||||
|
|
||||||
A basic OOT yaml will look like this. There are lots of cosmetic options that have been removed for the sake of this
|
A basic OoT yaml will look like this. There are lots of cosmetic options that have been removed for the sake of this
|
||||||
tutorial, if you want to see a complete list, download Archipelago from
|
tutorial, if you want to see a complete list, download Archipelago from
|
||||||
the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases) and look for the sample file in
|
the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases) and look for the sample file in
|
||||||
the "Players" folder.
|
the "Players" folder.
|
||||||
|
@ -382,20 +395,23 @@ Ocarina of Time:
|
||||||
|
|
||||||
When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done,
|
When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done,
|
||||||
the host will provide you with either a link to download your data file, or with a zip file containing everyone's data
|
the host will provide you with either a link to download your data file, or with a zip file containing everyone's data
|
||||||
files. Your data file should have a `.z5ap` extension.
|
files. Your data file should have a `.apz5` extension.
|
||||||
|
|
||||||
Double-click on your `.z5ap` file to start Z5Client and start the ROM patch process. Once the process is finished (this
|
Double-click on your `.apz5` file to start your client and start the ROM patch process. Once the process is finished (this
|
||||||
can take a while), the emulator will be started automatically (If we associated the extension to the emulator as
|
can take a while), the client and the emulator will be started automatically (if you associated the extension to the emulator as
|
||||||
recommended)
|
recommended).
|
||||||
|
|
||||||
### Connect to the Multiserver
|
### Connect to the Multiserver
|
||||||
|
|
||||||
Once both the Z5Client and the emulator are started we must connect them. Within the emulator we click on the "Tools"
|
Once both the client and the emulator are started, you must connect them. Within the emulator click on the "Tools"
|
||||||
menu and select "Lua console". In the new window click on the folder icon and look for the ootMulti.lua file. Once the
|
menu and select "Lua Console". Click the folder button or press Ctrl+O to open a Lua script.
|
||||||
file is loaded it will connect automatically to Z5Client.
|
|
||||||
|
|
||||||
Note: We strongly advise you don't open any emulator menu while it and Z5client are connected, as the script will halt
|
If you are using the Archipelago OoTClient, navigate to your Archipelago install folder and open `data/lua/OOT/oot_connector.lua`.
|
||||||
and disconnects can happen. If you get disconnected just double-click on the script again.
|
|
||||||
|
If you are using Z5Client, find the `ootMulti.lua` file and open it.
|
||||||
|
|
||||||
|
Note: If using Z5Client, we strongly advise you don't open any menus in Bizhawk while the emulator and Z5Client are connected, as the script will halt
|
||||||
|
and force-disconnect from the server. If you get disconnected just double-click on the script again.
|
||||||
|
|
||||||
To connect the client to the multiserver simply put `<address>:<port>` on the textfield on top and press enter (if the
|
To connect the client to the multiserver simply put `<address>:<port>` on the textfield on top and press enter (if the
|
||||||
server uses password, type in the bottom textfield `/connect <address>:<port> [password]`)
|
server uses password, type in the bottom textfield `/connect <address>:<port> [password]`)
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,380 @@
|
||||||
|
--
|
||||||
|
-- json.lua
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2015 rxi
|
||||||
|
--
|
||||||
|
-- This library is free software; you can redistribute it and/or modify it
|
||||||
|
-- under the terms of the MIT license. See LICENSE for details.
|
||||||
|
--
|
||||||
|
|
||||||
|
local json = { _version = "0.1.0" }
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Encode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local encode
|
||||||
|
|
||||||
|
local escape_char_map = {
|
||||||
|
[ "\\" ] = "\\\\",
|
||||||
|
[ "\"" ] = "\\\"",
|
||||||
|
[ "\b" ] = "\\b",
|
||||||
|
[ "\f" ] = "\\f",
|
||||||
|
[ "\n" ] = "\\n",
|
||||||
|
[ "\r" ] = "\\r",
|
||||||
|
[ "\t" ] = "\\t",
|
||||||
|
}
|
||||||
|
|
||||||
|
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||||
|
for k, v in pairs(escape_char_map) do
|
||||||
|
escape_char_map_inv[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function escape_char(c)
|
||||||
|
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_nil(val)
|
||||||
|
return "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_table(val, stack)
|
||||||
|
local res = {}
|
||||||
|
stack = stack or {}
|
||||||
|
|
||||||
|
-- Circular reference?
|
||||||
|
if stack[val] then error("circular reference") end
|
||||||
|
|
||||||
|
stack[val] = true
|
||||||
|
|
||||||
|
if val[1] ~= nil or next(val) == nil then
|
||||||
|
-- Treat as array -- check keys are valid and it is not sparse
|
||||||
|
local n = 0
|
||||||
|
for k in pairs(val) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if n ~= #val then
|
||||||
|
error("invalid table: sparse array")
|
||||||
|
end
|
||||||
|
-- Encode
|
||||||
|
for i, v in ipairs(val) do
|
||||||
|
table.insert(res, encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "[" .. table.concat(res, ",") .. "]"
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Treat as an object
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if type(k) ~= "string" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "{" .. table.concat(res, ",") .. "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_string(val)
|
||||||
|
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_number(val)
|
||||||
|
-- Check for NaN, -inf and inf
|
||||||
|
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||||
|
error("unexpected number value '" .. tostring(val) .. "'")
|
||||||
|
end
|
||||||
|
return string.format("%.14g", val)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local type_func_map = {
|
||||||
|
[ "nil" ] = encode_nil,
|
||||||
|
[ "table" ] = encode_table,
|
||||||
|
[ "string" ] = encode_string,
|
||||||
|
[ "number" ] = encode_number,
|
||||||
|
[ "boolean" ] = tostring,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
encode = function(val, stack)
|
||||||
|
local t = type(val)
|
||||||
|
local f = type_func_map[t]
|
||||||
|
if f then
|
||||||
|
return f(val, stack)
|
||||||
|
end
|
||||||
|
error("unexpected type '" .. t .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.encode(val)
|
||||||
|
return ( encode(val) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Decode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local parse
|
||||||
|
|
||||||
|
local function create_set(...)
|
||||||
|
local res = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
res[ select(i, ...) ] = true
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||||
|
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||||
|
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||||
|
local literals = create_set("true", "false", "null")
|
||||||
|
|
||||||
|
local literal_map = {
|
||||||
|
[ "true" ] = true,
|
||||||
|
[ "false" ] = false,
|
||||||
|
[ "null" ] = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local function next_char(str, idx, set, negate)
|
||||||
|
for i = idx, #str do
|
||||||
|
if set[str:sub(i, i)] ~= negate then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #str + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function decode_error(str, idx, msg)
|
||||||
|
--local line_count = 1
|
||||||
|
--local col_count = 1
|
||||||
|
--for i = 1, idx - 1 do
|
||||||
|
-- col_count = col_count + 1
|
||||||
|
-- if str:sub(i, i) == "\n" then
|
||||||
|
-- line_count = line_count + 1
|
||||||
|
-- col_count = 1
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
-- emu.message( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function codepoint_to_utf8(n)
|
||||||
|
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||||
|
local f = math.floor
|
||||||
|
if n <= 0x7f then
|
||||||
|
return string.char(n)
|
||||||
|
elseif n <= 0x7ff then
|
||||||
|
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||||
|
elseif n <= 0xffff then
|
||||||
|
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
elseif n <= 0x10ffff then
|
||||||
|
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||||
|
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
end
|
||||||
|
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_unicode_escape(s)
|
||||||
|
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||||
|
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||||
|
-- Surrogate pair?
|
||||||
|
if n2 then
|
||||||
|
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||||
|
else
|
||||||
|
return codepoint_to_utf8(n1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_string(str, i)
|
||||||
|
local has_unicode_escape = false
|
||||||
|
local has_surrogate_escape = false
|
||||||
|
local has_escape = false
|
||||||
|
local last
|
||||||
|
for j = i + 1, #str do
|
||||||
|
local x = str:byte(j)
|
||||||
|
|
||||||
|
if x < 32 then
|
||||||
|
decode_error(str, j, "control character in string")
|
||||||
|
end
|
||||||
|
|
||||||
|
if last == 92 then -- "\\" (escape char)
|
||||||
|
if x == 117 then -- "u" (unicode escape sequence)
|
||||||
|
local hex = str:sub(j + 1, j + 5)
|
||||||
|
if not hex:find("%x%x%x%x") then
|
||||||
|
decode_error(str, j, "invalid unicode escape in string")
|
||||||
|
end
|
||||||
|
if hex:find("^[dD][89aAbB]") then
|
||||||
|
has_surrogate_escape = true
|
||||||
|
else
|
||||||
|
has_unicode_escape = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local c = string.char(x)
|
||||||
|
if not escape_chars[c] then
|
||||||
|
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||||
|
end
|
||||||
|
has_escape = true
|
||||||
|
end
|
||||||
|
last = nil
|
||||||
|
|
||||||
|
elseif x == 34 then -- '"' (end of string)
|
||||||
|
local s = str:sub(i + 1, j - 1)
|
||||||
|
if has_surrogate_escape then
|
||||||
|
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_unicode_escape then
|
||||||
|
s = s:gsub("\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_escape then
|
||||||
|
s = s:gsub("\\.", escape_char_map_inv)
|
||||||
|
end
|
||||||
|
return s, j + 1
|
||||||
|
|
||||||
|
else
|
||||||
|
last = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
decode_error(str, i, "expected closing quote for string")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_number(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local s = str:sub(i, x - 1)
|
||||||
|
local n = tonumber(s)
|
||||||
|
if not n then
|
||||||
|
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||||
|
end
|
||||||
|
return n, x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_literal(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local word = str:sub(i, x - 1)
|
||||||
|
if not literals[word] then
|
||||||
|
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||||
|
end
|
||||||
|
return literal_map[word], x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_array(str, i)
|
||||||
|
local res = {}
|
||||||
|
local n = 1
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local x
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of array?
|
||||||
|
if str:sub(i, i) == "]" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read token
|
||||||
|
x, i = parse(str, i)
|
||||||
|
res[n] = x
|
||||||
|
n = n + 1
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "]" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_object(str, i)
|
||||||
|
local res = {}
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local key, val
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of object?
|
||||||
|
if str:sub(i, i) == "}" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read key
|
||||||
|
if str:sub(i, i) ~= '"' then
|
||||||
|
decode_error(str, i, "expected string for key")
|
||||||
|
end
|
||||||
|
key, i = parse(str, i)
|
||||||
|
-- Read ':' delimiter
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
if str:sub(i, i) ~= ":" then
|
||||||
|
decode_error(str, i, "expected ':' after key")
|
||||||
|
end
|
||||||
|
i = next_char(str, i + 1, space_chars, true)
|
||||||
|
-- Read value
|
||||||
|
val, i = parse(str, i)
|
||||||
|
-- Set
|
||||||
|
res[key] = val
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "}" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local char_func_map = {
|
||||||
|
[ '"' ] = parse_string,
|
||||||
|
[ "0" ] = parse_number,
|
||||||
|
[ "1" ] = parse_number,
|
||||||
|
[ "2" ] = parse_number,
|
||||||
|
[ "3" ] = parse_number,
|
||||||
|
[ "4" ] = parse_number,
|
||||||
|
[ "5" ] = parse_number,
|
||||||
|
[ "6" ] = parse_number,
|
||||||
|
[ "7" ] = parse_number,
|
||||||
|
[ "8" ] = parse_number,
|
||||||
|
[ "9" ] = parse_number,
|
||||||
|
[ "-" ] = parse_number,
|
||||||
|
[ "t" ] = parse_literal,
|
||||||
|
[ "f" ] = parse_literal,
|
||||||
|
[ "n" ] = parse_literal,
|
||||||
|
[ "[" ] = parse_array,
|
||||||
|
[ "{" ] = parse_object,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parse = function(str, idx)
|
||||||
|
local chr = str:sub(idx, idx)
|
||||||
|
local f = char_func_map[chr]
|
||||||
|
if f then
|
||||||
|
return f(str, idx)
|
||||||
|
end
|
||||||
|
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.decode(str)
|
||||||
|
if type(str) ~= "string" then
|
||||||
|
error("expected argument of type string, got " .. type(str))
|
||||||
|
end
|
||||||
|
return ( parse(str, next_char(str, 1, space_chars, true)) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return json
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,132 @@
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- LuaSocket helper module
|
||||||
|
-- Author: Diego Nehab
|
||||||
|
-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Declare module and import dependencies
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
local base = _G
|
||||||
|
local string = require("string")
|
||||||
|
local math = require("math")
|
||||||
|
local socket = require("socket.core")
|
||||||
|
module("socket")
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Exported auxiliar functions
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
function connect(address, port, laddress, lport)
|
||||||
|
local sock, err = socket.tcp()
|
||||||
|
if not sock then return nil, err end
|
||||||
|
if laddress then
|
||||||
|
local res, err = sock:bind(laddress, lport, -1)
|
||||||
|
if not res then return nil, err end
|
||||||
|
end
|
||||||
|
local res, err = sock:connect(address, port)
|
||||||
|
if not res then return nil, err end
|
||||||
|
return sock
|
||||||
|
end
|
||||||
|
|
||||||
|
function bind(host, port, backlog)
|
||||||
|
local sock, err = socket.tcp()
|
||||||
|
if not sock then return nil, err end
|
||||||
|
sock:setoption("reuseaddr", true)
|
||||||
|
local res, err = sock:bind(host, port)
|
||||||
|
if not res then return nil, err end
|
||||||
|
res, err = sock:listen(backlog)
|
||||||
|
if not res then return nil, err end
|
||||||
|
return sock
|
||||||
|
end
|
||||||
|
|
||||||
|
try = newtry()
|
||||||
|
|
||||||
|
function choose(table)
|
||||||
|
return function(name, opt1, opt2)
|
||||||
|
if base.type(name) ~= "string" then
|
||||||
|
name, opt1, opt2 = "default", name, opt1
|
||||||
|
end
|
||||||
|
local f = table[name or "nil"]
|
||||||
|
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
|
||||||
|
else return f(opt1, opt2) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Socket sources and sinks, conforming to LTN12
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- create namespaces inside LuaSocket namespace
|
||||||
|
sourcet = {}
|
||||||
|
sinkt = {}
|
||||||
|
|
||||||
|
BLOCKSIZE = 2048
|
||||||
|
|
||||||
|
sinkt["close-when-done"] = function(sock)
|
||||||
|
return base.setmetatable({
|
||||||
|
getfd = function() return sock:getfd() end,
|
||||||
|
dirty = function() return sock:dirty() end
|
||||||
|
}, {
|
||||||
|
__call = function(self, chunk, err)
|
||||||
|
if not chunk then
|
||||||
|
sock:close()
|
||||||
|
return 1
|
||||||
|
else return sock:send(chunk) end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
sinkt["keep-open"] = function(sock)
|
||||||
|
return base.setmetatable({
|
||||||
|
getfd = function() return sock:getfd() end,
|
||||||
|
dirty = function() return sock:dirty() end
|
||||||
|
}, {
|
||||||
|
__call = function(self, chunk, err)
|
||||||
|
if chunk then return sock:send(chunk)
|
||||||
|
else return 1 end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
sinkt["default"] = sinkt["keep-open"]
|
||||||
|
|
||||||
|
sink = choose(sinkt)
|
||||||
|
|
||||||
|
sourcet["by-length"] = function(sock, length)
|
||||||
|
return base.setmetatable({
|
||||||
|
getfd = function() return sock:getfd() end,
|
||||||
|
dirty = function() return sock:dirty() end
|
||||||
|
}, {
|
||||||
|
__call = function()
|
||||||
|
if length <= 0 then return nil end
|
||||||
|
local size = math.min(socket.BLOCKSIZE, length)
|
||||||
|
local chunk, err = sock:receive(size)
|
||||||
|
if err then return nil, err end
|
||||||
|
length = length - string.len(chunk)
|
||||||
|
return chunk
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
sourcet["until-closed"] = function(sock)
|
||||||
|
local done
|
||||||
|
return base.setmetatable({
|
||||||
|
getfd = function() return sock:getfd() end,
|
||||||
|
dirty = function() return sock:dirty() end
|
||||||
|
}, {
|
||||||
|
__call = function()
|
||||||
|
if done then return nil end
|
||||||
|
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
|
||||||
|
if not err then return chunk
|
||||||
|
elseif err == "closed" then
|
||||||
|
sock:close()
|
||||||
|
done = 1
|
||||||
|
return partial
|
||||||
|
else return nil, err end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
sourcet["default"] = sourcet["until-closed"]
|
||||||
|
|
||||||
|
source = choose(sourcet)
|
|
@ -108,6 +108,10 @@ minecraft_options:
|
||||||
oot_options:
|
oot_options:
|
||||||
# File name of the OoT v1.0 ROM
|
# File name of the OoT v1.0 ROM
|
||||||
rom_file: "The Legend of Zelda - Ocarina of Time.z64"
|
rom_file: "The Legend of Zelda - Ocarina of Time.z64"
|
||||||
|
# Set this to false to never autostart a rom (such as after patching)
|
||||||
|
# true for operating system default program
|
||||||
|
# Alternatively, a path to a program to open the .z64 file with
|
||||||
|
rom_start: true
|
||||||
soe_options:
|
soe_options:
|
||||||
# File name of the SoE US ROM
|
# File name of the SoE US ROM
|
||||||
rom_file: "Secret of Evermore (USA).sfc"
|
rom_file: "Secret of Evermore (USA).sfc"
|
||||||
|
|
|
@ -64,7 +64,7 @@ Name: "client/sni/lttp"; Description: "SNI Client - A Link to the Past Patch Se
|
||||||
Name: "client/sni/sm"; Description: "SNI Client - Super Metroid Patch Setup"; Types: full playing; Flags: disablenouninstallwarning
|
Name: "client/sni/sm"; Description: "SNI Client - Super Metroid Patch Setup"; Types: full playing; Flags: disablenouninstallwarning
|
||||||
Name: "client/factorio"; Description: "Factorio"; Types: full playing
|
Name: "client/factorio"; Description: "Factorio"; Types: full playing
|
||||||
Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278
|
Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278
|
||||||
Name: "client/oot"; Description: "Ocarina of Time Adjuster"; Types: full playing
|
Name: "client/oot"; Description: "Ocarina of Time"; Types: full playing
|
||||||
Name: "client/ff1"; Description: "Final Fantasy 1"; Types: full playing
|
Name: "client/ff1"; Description: "Final Fantasy 1"; Types: full playing
|
||||||
Name: "client/text"; Description: "Text, to !command and chat"; Types: full playing
|
Name: "client/text"; Description: "Text, to !command and chat"; Types: full playing
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ NAME: "{app}"; Flags: setntfscompression; Permissions: everyone-modify users-mod
|
||||||
Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/sni/lttp or generator/lttp
|
Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/sni/lttp or generator/lttp
|
||||||
Source: "{code:GetSMROMPath}"; DestDir: "{app}"; DestName: "Super Metroid (JU).sfc"; Flags: external; Components: client/sni/sm or generator/sm
|
Source: "{code:GetSMROMPath}"; DestDir: "{app}"; DestName: "Super Metroid (JU).sfc"; Flags: external; Components: client/sni/sm or generator/sm
|
||||||
Source: "{code:GetSoEROMPath}"; DestDir: "{app}"; DestName: "Secret of Evermore (USA).sfc"; Flags: external; Components: generator/soe
|
Source: "{code:GetSoEROMPath}"; DestDir: "{app}"; DestName: "Secret of Evermore (USA).sfc"; Flags: external; Components: generator/soe
|
||||||
Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda - Ocarina of Time.z64"; Flags: external; Components: generator/oot
|
Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda - Ocarina of Time.z64"; Flags: external; Components: client/oot or generator/oot
|
||||||
Source: "{#source_path}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI, Archipelago*.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "{#source_path}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI, Archipelago*.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
Source: "{#source_path}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/sni
|
Source: "{#source_path}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/sni
|
||||||
Source: "{#source_path}\EnemizerCLI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\EnemizerCLI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: generator/lttp
|
Source: "{#source_path}\EnemizerCLI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\EnemizerCLI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: generator/lttp
|
||||||
|
@ -87,6 +87,7 @@ Source: "{#source_path}\ArchipelagoTextClient.exe"; DestDir: "{app}"; Flags: ign
|
||||||
Source: "{#source_path}\ArchipelagoSNIClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni
|
Source: "{#source_path}\ArchipelagoSNIClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni
|
||||||
Source: "{#source_path}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni/lttp or generator/lttp
|
Source: "{#source_path}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni/lttp or generator/lttp
|
||||||
Source: "{#source_path}\ArchipelagoMinecraftClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/minecraft
|
Source: "{#source_path}\ArchipelagoMinecraftClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/minecraft
|
||||||
|
Source: "{#source_path}\ArchipelagoOoTClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/oot
|
||||||
Source: "{#source_path}\ArchipelagoOoTAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/oot
|
Source: "{#source_path}\ArchipelagoOoTAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/oot
|
||||||
Source: "{#source_path}\ArchipelagoFF1Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/ff1
|
Source: "{#source_path}\ArchipelagoFF1Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/ff1
|
||||||
Source: "vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall
|
Source: "vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall
|
||||||
|
@ -98,13 +99,16 @@ Name: "{group}\{#MyAppName} Text Client"; Filename: "{app}\ArchipelagoTextClient
|
||||||
Name: "{group}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Components: client/sni
|
Name: "{group}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Components: client/sni
|
||||||
Name: "{group}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Components: client/factorio
|
Name: "{group}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Components: client/factorio
|
||||||
Name: "{group}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Components: client/minecraft
|
Name: "{group}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Components: client/minecraft
|
||||||
|
Name: "{group}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Components: client/oot
|
||||||
Name: "{group}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Components: client/ff1
|
Name: "{group}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Components: client/ff1
|
||||||
|
|
||||||
Name: "{commondesktop}\{#MyAppName} Folder"; Filename: "{app}"; Tasks: desktopicon
|
Name: "{commondesktop}\{#MyAppName} Folder"; Filename: "{app}"; Tasks: desktopicon
|
||||||
Name: "{commondesktop}\{#MyAppName} Server"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; Components: server
|
Name: "{commondesktop}\{#MyAppName} Server"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; Components: server
|
||||||
Name: "{commondesktop}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Tasks: desktopicon; Components: client/sni
|
Name: "{commondesktop}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Tasks: desktopicon; Components: client/sni
|
||||||
Name: "{commondesktop}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Tasks: desktopicon; Components: client/factorio
|
Name: "{commondesktop}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Tasks: desktopicon; Components: client/factorio
|
||||||
Name: "{commondesktop}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Tasks: desktopicon; Components: client/factorio
|
Name: "{commondesktop}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Tasks: desktopicon; Components: client/minecraft
|
||||||
|
Name: "{commondesktop}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Tasks: desktopicon; Components: client/oot
|
||||||
|
Name: "{commondesktop}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Tasks: desktopicon; Components: client/ff1
|
||||||
|
|
||||||
[Run]
|
[Run]
|
||||||
|
|
||||||
|
@ -140,6 +144,11 @@ Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archip
|
||||||
Root: HKCR; Subkey: "{#MyAppName}mcdata\DefaultIcon"; ValueData: "{app}\ArchipelagoMinecraftClient.exe,0"; ValueType: string; ValueName: ""; Components: client/minecraft
|
Root: HKCR; Subkey: "{#MyAppName}mcdata\DefaultIcon"; ValueData: "{app}\ArchipelagoMinecraftClient.exe,0"; ValueType: string; ValueName: ""; Components: client/minecraft
|
||||||
Root: HKCR; Subkey: "{#MyAppName}mcdata\shell\open\command"; ValueData: """{app}\ArchipelagoMinecraftClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/minecraft
|
Root: HKCR; Subkey: "{#MyAppName}mcdata\shell\open\command"; ValueData: """{app}\ArchipelagoMinecraftClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/minecraft
|
||||||
|
|
||||||
|
Root: HKCR; Subkey: ".apz5"; ValueData: "{#MyAppName}n64zpf"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/oot
|
||||||
|
Root: HKCR; Subkey: "{#MyAppName}n64zpf"; ValueData: "Archipelago Ocarina of Time Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/oot
|
||||||
|
Root: HKCR; Subkey: "{#MyAppName}n64zpf\DefaultIcon"; ValueData: "{app}\ArchipelagoOoTClient.exe,0"; ValueType: string; ValueName: ""; Components: client/oot
|
||||||
|
Root: HKCR; Subkey: "{#MyAppName}n64zpf\shell\open\command"; ValueData: """{app}\ArchipelagoOoTClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/oot
|
||||||
|
|
||||||
Root: HKCR; Subkey: ".archipelago"; ValueData: "{#MyAppName}multidata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: server
|
Root: HKCR; Subkey: ".archipelago"; ValueData: "{#MyAppName}multidata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: server
|
||||||
Root: HKCR; Subkey: "{#MyAppName}multidata"; ValueData: "Archipelago Server Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: server
|
Root: HKCR; Subkey: "{#MyAppName}multidata"; ValueData: "Archipelago Server Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: server
|
||||||
Root: HKCR; Subkey: "{#MyAppName}multidata\DefaultIcon"; ValueData: "{app}\ArchipelagoServer.exe,0"; ValueType: string; ValueName: ""; Components: server
|
Root: HKCR; Subkey: "{#MyAppName}multidata\DefaultIcon"; ValueData: "{app}\ArchipelagoServer.exe,0"; ValueType: string; ValueName: ""; Components: server
|
||||||
|
|
6
kvui.py
6
kvui.py
|
@ -406,6 +406,12 @@ class FF1Manager(GameManager):
|
||||||
]
|
]
|
||||||
base_title = "Archipelago Final Fantasy 1 Client"
|
base_title = "Archipelago Final Fantasy 1 Client"
|
||||||
|
|
||||||
|
class OoTManager(GameManager):
|
||||||
|
logging_pairs = [
|
||||||
|
("Client", "Archipelago")
|
||||||
|
]
|
||||||
|
base_title = "Archipelago Ocarina of Time Client"
|
||||||
|
|
||||||
|
|
||||||
class LogtoUI(logging.Handler):
|
class LogtoUI(logging.Handler):
|
||||||
def __init__(self, on_log):
|
def __init__(self, on_log):
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -91,6 +91,7 @@ scripts = {
|
||||||
# Minecraft
|
# Minecraft
|
||||||
"MinecraftClient.py": ("ArchipelagoMinecraftClient", False, mcicon),
|
"MinecraftClient.py": ("ArchipelagoMinecraftClient", False, mcicon),
|
||||||
# Ocarina of Time
|
# Ocarina of Time
|
||||||
|
"OoTClient.py": ("ArchipelagoOoTClient", True, icon),
|
||||||
"OoTAdjuster.py": ("ArchipelagoOoTAdjuster", True, icon),
|
"OoTAdjuster.py": ("ArchipelagoOoTAdjuster", True, icon),
|
||||||
# FF1
|
# FF1
|
||||||
"FF1Client.py": ("ArchipelagoFF1Client", True, icon),
|
"FF1Client.py": ("ArchipelagoFF1Client", True, icon),
|
||||||
|
|
Loading…
Reference in New Issue