335 lines
26 KiB
Python
335 lines
26 KiB
Python
"""Build the player package (dist/DS2Revival + dist/DS2Revival.zip). Players never run an installer: they unzip it into
|
|
the folder with DungeonSiege2.exe (drag and drop; also to update, replacing files) and delete files to turn things off.
|
|
|
|
usage:
|
|
python -I tools/package.py build dist/DS2Revival (+ .zip)
|
|
python -I tools/package.py --with-controller same plus an opt-in 'Optional - controller' folder (not installed)
|
|
python -I tools/package.py --install "E:\\...\\Dungeon Siege 2"
|
|
developer tool: copy what the zip holds into a game folder, as
|
|
extracting the zip there would
|
|
|
|
The zip holds version.dll (Ultimate ASI Loader) and the plugins in scripts\\ (each works on its own; deleting one turns it
|
|
off), a "Broken World" folder with version.dll and only scripts\\DS2Revival.asi (mods/revival_loader: it loads the main
|
|
scripts folder's plugins into the Broken World process - one set of plugins for both games; harmless without the expansion:
|
|
DS2BrokenWorld starts Broken World only when Broken World\\DungeonSiege2.exe exists), a readme, "Firewall setup.cmd" and a tool. It does NOT hold DS2Revival.ini: a
|
|
drag-and-drop update would overwrite the player's settings. The plugins create DS2Revival.ini next to DungeonSiege2.exe on their first start
|
|
from the default text compiled into them (mods/common/DS2Revival.ini is the one source; mods/build.py generates a header
|
|
from it), move the values of older per-plugin inis (DS2Multiplayer.ini, ...) into it and rename those to .old, and add the
|
|
settings a later version brings (mods/common/settings_file.h; tools/test_settings_ini.py tests it).
|
|
The plugins in the zip are those in mods/build.py RELEASE. --install never touches game files; it refuses to overwrite a
|
|
version.dll that is not the ASI loader. (Finer glow is done in memory by DS2Graphics; no data mod is written.)
|
|
"""
|
|
import hashlib, importlib.util, os, shutil, sys, zipfile
|
|
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
DIST = os.path.join(ROOT, 'dist', 'DS2Revival')
|
|
LOADER = os.path.join(ROOT, 'build', 'asiloader', 'dinput8.dll') # Ultimate ASI Loader (x86)
|
|
MODS = os.path.join(ROOT, 'build', 'mods')
|
|
|
|
|
|
def release_plugins():
|
|
"""The plugins of the release: mods/build.py RELEASE (one list, so a plugin ships as soon as it is in it)."""
|
|
spec = importlib.util.spec_from_file_location('ds2_mods_build', os.path.join(ROOT, 'mods', 'build.py'))
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
return list(mod.RELEASE), mod.BW_LOADER
|
|
|
|
|
|
PLUGINS, BW_LOADER = release_plugins()
|
|
BW_FOLDER = 'Broken World'
|
|
README_NAME = 'DS2Revival README.txt' # not README.txt: extracted into the game folder it must not replace a file of the game
|
|
|
|
README = """Dungeon Siege II revival (Steam v2.3, and Broken World)
|
|
=======================================================
|
|
|
|
Install and update: unzip this archive into your Dungeon Siege 2 folder (the one with DungeonSiege2.exe), or drag its
|
|
contents there. When you update to a newer version, do the same and allow replacing the files. That is all - there
|
|
is nothing else to run or set up. Your settings are not in the archive (the plugins create them, see "Settings" below), so
|
|
an update never overwrites them. Nothing of the game is modified.
|
|
|
|
To remove everything, delete version.dll and the scripts folder (also in the "Broken World" folder if you use it).
|
|
To turn one plugin off, delete its .asi file from the scripts folder.
|
|
|
|
Use it with the unmodified game: it is not meant to be combined with mods that replace DungeonSiege2.exe or
|
|
version.dll (e.g. Dungeon Siege II Remastered, OpenSpy's version.dll).
|
|
|
|
What is in it (every setting is explained in DS2Revival.ini, next to DungeonSiege2.exe, created at the first start):
|
|
Online Multiplayer and LAN without GameSpy, on our free server; no port forwarding; no DirectPlay install
|
|
(our own replacement is used when Windows has none: also Linux and Steam Deck); the game's own
|
|
"new patch" banner shows our news; a joiner whose game differs from the host's is told so and goes back to
|
|
the game list (Dungeon Siege II); the website dsii.uk has the Armory, 23 achievements, leaderboards
|
|
and a save history of your online parties (restore an older version on the account page).
|
|
Picture A borderless window at your desktop resolution, ultrawide included; the interface sized like a 720p
|
|
screen at every resolution (decimal scales too, with crisp text; item labels, the map and the loading
|
|
screen follow it); anti-aliasing, sharpening, anisotropic filtering, the Shaders fix, shadow size and
|
|
finer glow in any display mode, not only borderless; photo mode with a free camera; wider view and
|
|
zoom options; the mouse pointer in the game's own exclusive fullscreen; intro movies without a black flash; black
|
|
hero portraits above 1280 px fixed (also in Broken World).
|
|
Stability A crash in the engine's vertex upload; memory each newly visited region kept for good; graphics
|
|
options that silently switched off on AMD cards; Broken World multiplayer hosts running out of memory
|
|
on 1440p+ screens; save folders with Cyrillic or Chinese user names or a blocked Documents folder;
|
|
Cyrillic text with Windows' UTF-8 setting; plugin logs capped at 4 MB.
|
|
Broken World One plugin set and one settings file for both games; old saves load, the official editions' saves
|
|
(Steam, GOG exe, Broken World, with or without the 2018 Widescreen Fix) are recognised by name; the HUD
|
|
portrait at every resolution; no frozen joiner when the host presses START; the 2018 repack's
|
|
Widescreen Fix is retired so nobody is split from the other players; missing binkw32.dll / mss32.dll
|
|
are found in the main game's folder; Steam heroes import; the frame-rate fix.
|
|
Handy Plugins can be switched off in DS2Revival.ini ([Plugins]); updating = dragging the new files over the
|
|
old ones; the sign-up form and the minimum-spec warning are skipped; mods can say which game they are for.
|
|
Roadmap and what is planned next: https://dsii.uk/roadmap
|
|
Source code and releases: https://git.dsii.uk/dsii/DS2Revival
|
|
|
|
Every fix is a separate plugin in the scripts folder and works on its own. All are on by default;
|
|
delete a plugin's .asi file (or set it to 0 under [Plugins] in DS2Revival.ini) to turn it off.
|
|
|
|
version.dll Ultimate ASI Loader (loads the plugins)
|
|
scripts\\DS2Multiplayer.asi online and LAN play without GameSpy:
|
|
- re-enables the Multiplayer menu (the Steam build greys it out)
|
|
- points the game's online services at the revival server
|
|
- gives Steam copies a CD-key identity (per user; your registry's game keys
|
|
are never touched)
|
|
- fixes the bugs that stop a fresh session from hosting (START button,
|
|
lobby server, join timeout, Windows Firewall question, the lobby
|
|
staying on screen after leaving it); warns and offers to turn on Windows' DirectPlay feature
|
|
when DirectPlay (needed for online play) is missing
|
|
- up to 8 players: the game's hidden 8-player modes ("Old School Mode":
|
|
8 players with their main hero; "Couples Mode++": 6 players with two
|
|
characters) can be picked when hosting (only the host needs this)
|
|
- a joiner whose DungeonSiege2.exe differs from the host's is told what differs and
|
|
goes back to the game list instead of waiting in the staging area (JoinVersionMessage)
|
|
- relays games through the server when the host is behind a router, so no
|
|
port forwarding is needed
|
|
DS2Revival.ini all settings of all plugins, next to DungeonSiege2.exe: created at the first start (see "Settings" below)
|
|
scripts\\DS2Widescreen.asi the display: a borderless window at your desktop resolution (ultrawide
|
|
included): the world is rendered natively, the 800x600 menus are scaled to fit
|
|
with black bars. Works with every Steam launch option and with the Steam overlay;
|
|
a failed graphics reset (alt-tab, overlay) is retried instead of crashing the
|
|
game. The in-game interface is sized like a 720p screen at any resolution (HudScale,
|
|
decimals allowed; HudFilter=sharp keeps text crisp). Wide screens show more to the sides (the original cut the top and bottom off); optional camera
|
|
settings: zoom out further, flatter or top-down view. Photo mode (Insert) and the
|
|
NVIDIA RTX Remix profile are here too. The game's own exclusive fullscreen (Mode=off) gets its
|
|
mouse pointer back (ExclusiveCursor; Windows shows none there).
|
|
scripts\\DS2Graphics.asi the picture: anti-aliasing (4x multisampling plus SMAA, no dgVoodoo needed)
|
|
and sharpening of the 3D world (text and interface stay sharp; the 3D scenes of
|
|
the hero menus are smoothed too), anisotropic texture filtering, the Video option "Shaders"
|
|
that the game forgot at every start, optional SSAO, shadow size, finer glow (built
|
|
in memory, no files), and the black
|
|
hero portraits above 1280 pixels wide. Works on its own too, in any
|
|
window mode. Delete it for the game's own picture; DS2Widescreen then keeps
|
|
the window, interface size and photo mode (photo mode pictures then have no
|
|
anti-aliasing).
|
|
scripts\\DS2Fixes.asi stops the game from making typing lag in every other program while it runs,
|
|
and the stutter while moving or clicking the mouse; and makes the game's
|
|
vertex upload (shadows, particles, character meshes) stop reading up to
|
|
168 bytes past the end of its source data, which could crash it when that
|
|
data ends right at the end of the memory around it;
|
|
and keeps saves working: a Documents path with Cyrillic or Chinese letters is given
|
|
to the game as an ASCII path, and a blocked Documents folder is explained;
|
|
Cyrillic text works with Windows' UTF-8 setting (TextCodepage); skips the
|
|
Gas Powered Games sign-up form and the minimum-spec warning;
|
|
and gives back the animation memory of far-away objects: every region you
|
|
visit used to keep about 7 MB of it for the whole session, which filled the
|
|
game's 2 GB of address space after roughly 70 regions (on in Dungeon Siege II, off in
|
|
Broken World until it is proven there: DormantAnimations)
|
|
scripts\\DS2BrokenWorld.asi starts Broken World when it is installed (see below), lets it import
|
|
heroes made in the Steam game and old saves (and the official editions' saves,
|
|
by name: AcceptKnownEditions), retires the 2018 repack's
|
|
Widescreen Fix.ds2res, finds binkw32.dll / mss32.dll when its own are
|
|
missing, and fixes its low frame rate on PCs where Windows runs its
|
|
hypervisor (Memory Integrity, WSL2, Hyper-V, Docker...)
|
|
scripts\\DS2ModLoader.asi mods without touching the game's files (see "Mods" below)
|
|
|
|
Mods: make a "Mods" folder next to DungeonSiege2.exe and give every mod its own folder in it:
|
|
Mods\\<mod name>\\*.ds2res, *.ds2map archives (tanks), loaded on top of the game's own
|
|
Mods\\<mod name>\\art\\..., world\\..., ui\\... loose files: they replace the file with the same path inside
|
|
the game's archives (e.g. Mods\\MyShader\\world\\global\\effects\\
|
|
shaders\\glow.fx) - no packing needed
|
|
Mods\\*.ds2res, *.ds2map archives dropped straight into Mods are loaded too (before the folders)
|
|
Mods\\<mod name>\\art\\...\\NAME.glb a glTF model (.glb or .gltf, e.g. exported from Blender) replaces the
|
|
mesh NAME.asp at the same path - nothing to convert: the game uses it,
|
|
with its textures. A glTF outside art\\ (also straight in Mods) goes
|
|
where the game keeps the asset of that name. Skinned models work too;
|
|
their animations named after a .prs (a_...) replace that animation, and
|
|
a glTF named a_....glb is one animation. Name a material after a
|
|
texture (b_...) to replace that texture (e.g. one an object's
|
|
template sets). Converted copies are kept in Mods\\.cache and rebuilt
|
|
when the glTF changes; a glTF that cannot be read is skipped (logged)
|
|
and the game's own asset stays.
|
|
Mods override the game, Broken World's content and its patches. When two mods change the same file, the one
|
|
loaded later wins: mods load in folder-name order, or in the order of an optional Mods\\load_order.txt (one
|
|
folder name per line, later lines win; "-Name" turns a mod off; folders not listed load after the listed
|
|
ones). Inside one mod, its loose files win over its archives. Folders starting with "_" or "." are ignored.
|
|
Maps in Mods appear in the map list like the game's own. scripts\\logs\\DS2Revival.log (the lines tagged [DS2ModLoader]) lists every mod, its archives, which
|
|
mod overrides which, and what the game mounted (with [Logging] Detail=1 in DS2Revival.ini: every file too); broken archives are skipped (and logged), never fatal.
|
|
Note: a plain copy of one of the game's own archives in Mods is ignored by the game (it is already loaded);
|
|
put only the changed files in a mod, as loose files or in a new archive.
|
|
|
|
Which game a mod is for: Broken World loads the main Mods folder first, then its own "Broken World\\Mods".
|
|
- A mod in the main Mods folder is for both games, unless its folder holds a modinfo.ini:
|
|
[Mod]
|
|
Games=ds2 only the original game (Games=bw: only Broken World; Games=both: both)
|
|
- A mod in "Broken World\\Mods" is Broken World's by its place: the original game never loads it, and it
|
|
wins over the main folder's mods there.
|
|
- Without Games=, Broken World protects its own changes: a loose file of a main-folder mod (or a glTF file's
|
|
converted result) that has the same path as a file in Broken World's own archives (x*.ds2res - the
|
|
expansion changed it) is skipped, each one named in DS2Revival.log. Games=both says "this mod is
|
|
meant for both" and loads everything. Archives (.ds2res/.ds2map) are loaded whole or not at all.
|
|
- Files and archives straight in Mods (no folder) have no modinfo.ini: they load in both games, with the
|
|
protection above for glTF files. Put such a mod into a folder to give it a modinfo.ini.
|
|
Multiplayer: a skipped file does not count for the game's content check (a loose .gas file does when it loads).
|
|
|
|
Broken World (optional): the archive also puts version.dll and one small plugin, DS2Revival.asi, into a "Broken World"
|
|
folder (inside your Dungeon Siege 2 folder, next to scripts). Broken World runs the same plugins as the main game: DS2Revival.asi
|
|
loads them from the main scripts folder, so there is nothing extra to install and one plugin set to update. If you own the
|
|
expansion, put its files in that folder; starting the game then starts Broken World, hold Shift while it starts for the
|
|
original game. No registry patch is needed. From the expansion's folder only these are used:
|
|
DungeonSiege2.exe, system_detail.gas, system\\EBUEula.dll, Resources\\x*.ds2res, Maps\\x*.ds2map
|
|
(the other files duplicate the base game's or belong to the expansion's own setup program). A plugin deleted from the main
|
|
scripts folder is off for Broken World too, and it uses the main folder's settings. In DS2Revival.ini, [Plugins] DS2Graphics=ds2
|
|
keeps one plugin out of Broken World (1 = both games, 0 = off, ds2 = Dungeon Siege II only, bw = Broken World only).
|
|
If you do not own Broken World, the folder does nothing: the game is only started as Broken World when
|
|
Broken World\\DungeonSiege2.exe exists. You can delete the folder.
|
|
Updating from an older version: older archives put full copies of the plugins into Broken World\\scripts. Delete the .asi
|
|
files there except DS2Revival.asi; while they exist they run instead of the main folder's plugins (scripts\\logs\\DS2Revival.bw.log
|
|
in the main Dungeon Siege 2 folder says so).
|
|
A Broken World folder on its own (not inside the Dungeon Siege 2 folder): drag version.dll and the scripts folder of this
|
|
archive into the Broken World folder, the plugins work on their own there, and ignore the archive's "Broken World" folder.
|
|
|
|
Linux / Steam Deck (Proton): the plugins load through version.dll (the ASI loader). Wine and Valve's Proton may prefer their
|
|
own built-in version.dll, in which case nothing of this loads. In Steam, open the game's Properties, Launch Options and enter
|
|
WINEDLLOVERRIDES="version=n,b" %command%
|
|
(n,b = prefer the native version.dll in the game folder). The plugins' log appears in the game folder
|
|
(steamapps/common/Dungeon Siege 2/scripts/logs/DS2Revival.log); if there is none, the override is not applied. Broken World is
|
|
started by the game folder's launcher as on Windows (hold Shift for the original game) and needs the same launch option.
|
|
Multiplayer under Proton uses our DirectPlay replacement (DS2Revival.ini [Network] DirectPlay=auto picks it, because Proton's built-in
|
|
DirectPlay cannot host).
|
|
|
|
Difficulty unlock: tools\\ds2_unlock.py unlocks Veteran and Elite for a party, as if it had finished the
|
|
earlier difficulties (the game's own journal flags; the old save is kept as .unlockbak). Needs Python 3 from
|
|
python.org. Close the game, then drag the party's .ds2party file (Documents\\My Games\\Dungeon Siege 2\\Save\\...)
|
|
onto ds2_unlock.py. Run it with --help for the options (e.g. --to veteran, or --world ds2x_world for Broken World).
|
|
|
|
Firewall: double-click "Firewall setup.cmd" once (asks for administrator rights) so other players can
|
|
connect to games you host. Port forwarding is optional: with UDP 2302 forwarded to the hosting PC
|
|
players connect directly instead of through the relay (Relay=off in DS2Revival.ini [Network] to force that).
|
|
|
|
Settings: the first time the game starts, the plugins create DS2Revival.ini next to DungeonSiege2.exe, the one settings file of all plugins,
|
|
with every setting, its default value and a short comment (one section per plugin). Open it in Notepad, change a value,
|
|
restart the game. Delete a plugin's .asi to turn that plugin off completely; a section without its plugin does nothing.
|
|
Broken World started from the Dungeon Siege 2 folder uses the same file (a Broken World folder on its own keeps its own, next to its exe).
|
|
The file is not in the archive, so updating never overwrites your values. When a new
|
|
version has new settings they are added to your file, with their comments, at the next start; your values and your own
|
|
comments are never changed - except that when a version changes the DEFAULT of a setting (1.3: AntiAliasing smaa ->
|
|
msaa4+smaa), a value that is still the old default is changed to the new one, once (the log says so; the "[Settings]
|
|
Version" line at the end of the file keeps count). A value you chose yourself stays. If you had changed values in older per-plugin files (DS2Multiplayer.ini, DS2Widescreen.ini,
|
|
...), they are moved into DS2Revival.ini when it is first created and those files are renamed to .ini.old. To get the
|
|
default file back, delete DS2Revival.ini: it is created again at the next start.
|
|
Versions 1.2.0 and 1.2.1 kept the file in the scripts folder; the first start of this version moves it next to DungeonSiege2.exe.
|
|
|
|
Logs: all plugins write one log file per game start, scripts\\logs\\DS2Revival.log (DS2Revival.bw.log for Broken World), each line
|
|
tagged with the plugin; the last three runs are kept (DS2Revival.1.log, ...). Please send DS2Revival.log (and .bw.log) when you
|
|
report a problem. [Logging] Detail=1 in DS2Revival.ini makes it much longer (every patch address) - only when asked.
|
|
"""
|
|
|
|
|
|
def copy_crlf(src, folder, name=None):
|
|
"""Copy a text file for players with Windows line endings, whatever the checkout's (a Linux CI checkout
|
|
has LF)."""
|
|
data = open(src, 'rb').read().replace(b'\r\n', b'\n').replace(b'\n', b'\r\n')
|
|
with open(os.path.join(folder, name or os.path.basename(src)), 'wb') as f:
|
|
f.write(data)
|
|
|
|
|
|
CONTROLLER_README = """DS2Revival controller support (preview, optional)
|
|
|
|
Not installed by default. To try it: close the game, then copy DS2Controller.asi and DS2Controller.ini from this folder
|
|
into the game's scripts folder. Broken World loads it from there too (Broken World\\scripts\\DS2Revival.asi runs the main
|
|
folder's plugins) and reads the same DS2Controller.ini. Start the game from
|
|
Steam and use an Xbox, PlayStation (4/5), Switch Pro or other standard pad; with Steam Input choose a Gamepad layout (not
|
|
a keyboard/mouse or desktop one), or switch Steam Input off for the game. The button prompts follow the pad you hold and show only
|
|
while the pad is in use. Delete DS2Controller.asi to remove it. The pad never steers a mouse pointer: menus use a focus
|
|
highlight, the world uses the sticks, A/B/X/Y and the LB/RB/Back wheels. Controls and settings are listed in DS2Controller.ini (the controller keeps its own settings file; it is not part of DS2Revival.ini).
|
|
Contains SDL3 (zlib licence), see THIRD-PARTY-NOTICES.txt.
|
|
"""
|
|
|
|
|
|
def add_loader_and_plugins(folder, plugins):
|
|
"""version.dll and scripts\\<plugins>.asi in folder (the game folder, and the Broken World folder)."""
|
|
os.makedirs(os.path.join(folder, 'scripts'))
|
|
shutil.copy(LOADER, os.path.join(folder, 'version.dll'))
|
|
for p in plugins:
|
|
shutil.copy(os.path.join(MODS, p + '.asi'), os.path.join(folder, 'scripts'))
|
|
|
|
|
|
def build(with_controller=False):
|
|
if os.path.isdir(DIST):
|
|
shutil.rmtree(DIST)
|
|
os.makedirs(DIST)
|
|
add_loader_and_plugins(DIST, PLUGINS) # the game folder: version.dll + scripts\*.asi
|
|
# Broken World: the loader and DS2Revival.asi, which loads the main folder's plugins into the Broken World process
|
|
add_loader_and_plugins(os.path.join(DIST, BW_FOLDER), [BW_LOADER])
|
|
with open(os.path.join(DIST, README_NAME), 'w', newline='\r\n') as f:
|
|
f.write(README)
|
|
copy_crlf(os.path.join(ROOT, 'tools', 'firewall-setup.ps1'), DIST)
|
|
os.makedirs(os.path.join(DIST, 'tools'))
|
|
shutil.copy(os.path.join(ROOT, 'tools', 'ds2_unlock.py'), os.path.join(DIST, 'tools'))
|
|
copy_crlf(os.path.join(ROOT, 'tools', 'third_party', 'THIRD-PARTY-NOTICES.txt'), DIST)
|
|
if with_controller: # opt-in preview: a folder beside scripts\, never loaded until the player copies it in
|
|
opt = os.path.join(DIST, 'Optional - controller')
|
|
os.makedirs(opt)
|
|
shutil.copy(os.path.join(MODS, 'DS2Controller.asi'), opt)
|
|
copy_crlf(os.path.join(ROOT, 'mods', 'controller', 'DS2Controller.ini'), opt)
|
|
with open(os.path.join(opt, 'README.txt'), 'w', newline='\r\n') as f:
|
|
f.write(CONTROLLER_README)
|
|
with open(os.path.join(DIST, 'Firewall setup.cmd'), 'w', newline='\r\n') as f:
|
|
f.write('@echo off\npowershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0firewall-setup.ps1" -GameDir "%~dp0."\n')
|
|
z = DIST + '.zip'
|
|
with zipfile.ZipFile(z, 'w', zipfile.ZIP_DEFLATED) as zf:
|
|
for base, _, files in sorted(os.walk(DIST)):
|
|
for n in sorted(files):
|
|
full = os.path.join(base, n)
|
|
zf.write(full, os.path.relpath(full, DIST)) # files at the root: extract straight into the game folder
|
|
print('built', DIST, 'and', z)
|
|
|
|
|
|
def sha(p):
|
|
return hashlib.sha256(open(p, 'rb').read()).hexdigest()
|
|
|
|
|
|
def ours(folder):
|
|
vd = os.path.join(folder, 'version.dll')
|
|
return not os.path.exists(vd) or sha(vd) == sha(os.path.join(DIST, 'version.dll'))
|
|
|
|
|
|
def install(game):
|
|
"""What extracting the zip into the game folder does (a developer tool: players unzip it themselves). The
|
|
Broken World part only where Broken World's exe is, so that a test folder does not gain one."""
|
|
if not os.path.isfile(os.path.join(game, 'DungeonSiege2.exe')):
|
|
sys.exit('no DungeonSiege2.exe in %s' % game)
|
|
if not ours(game):
|
|
sys.exit('%s exists and is not our ASI loader - not overwriting' % os.path.join(game, 'version.dll'))
|
|
skip_bw = not os.path.isfile(os.path.join(game, BW_FOLDER, 'DungeonSiege2.exe'))
|
|
if not skip_bw and not ours(os.path.join(game, BW_FOLDER)):
|
|
print('note: %s is not our ASI loader - Broken World left alone' % os.path.join(game, BW_FOLDER, 'version.dll'))
|
|
skip_bw = True
|
|
for base, dirs, files in os.walk(DIST):
|
|
rel = os.path.relpath(base, DIST)
|
|
if rel == BW_FOLDER or rel.startswith(BW_FOLDER + os.sep):
|
|
if skip_bw:
|
|
continue
|
|
dst = game if rel == '.' else os.path.join(game, rel)
|
|
os.makedirs(dst, exist_ok=True)
|
|
for n in files:
|
|
shutil.copy(os.path.join(base, n), os.path.join(dst, n))
|
|
print('copied the package into', game, '(DS2Revival.ini is created by the plugins at the first start)')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
a = sys.argv[1:]
|
|
build('--with-controller' in a)
|
|
if '--install' in a:
|
|
try:
|
|
install(a[a.index('--install') + 1])
|
|
except PermissionError as e:
|
|
sys.exit('cannot replace %s - close the game (and Broken World) and try again' % e.filename)
|