Git Product home page Git Product logo

angelloader's Introduction

AngelLoader


Thanks to JetBrains for providing their tools to AngelLoader under their Open Source License.

Description

AngelLoader is a standalone fan mission loader for Thief 1, Thief 2, Thief 3, System Shock 2, and The Dark Mod. Unlike FMSel, which requires a separate copy for each game, AngelLoader allows you to manage and play all your FMs in one place. The interface is inspired by DarkLoader (by Björn Henke and Tom N. Harris) and NewDarkLoader (by Robin Collier).

Features

  • Manage all your FMs in one place
  • Select multiple FMs at a time to quickly mark them all finished, install them in bulk, etc.
  • Search and filter your FM collection by many criteria
  • Automatically detects metadata for every FM: title, author, game, release date, etc. No need to type it in!
  • Organize your games by tab or treat them as filters
  • Disable visual or other mods on a per-FM basis with a simple visual list (no more typing an arcane string into a textbox!)
  • Choose which language to play with on a per-FM basis
  • Import your FM information from DarkLoader, NewDarkLoader, or FMSel
  • Automatically fixes common problems, such as:
    • Non-16-bit audio causing static
    • Custom palettes not being applied properly to FMs (Ranstall Keep etc.)
    • Local values left in global config files by NewDark (character_detail)
  • Option to automatically use old mantling for OldDark missions
  • Supports light and dark themes
  • Plays nice with other loaders: AngelLoader doesn't store any .dlls or data files in your game folders, making it truly portable and non-intrusive

Installing

Simply download the latest release and unzip it to a folder of your choice. For example, C:\AngelLoader. New versions can be extracted right on top of old ones: your data files will not be overwritten.

Note: Some folders are considered "protected" (Program Files / Program Files (x86) are examples) and AngelLoader should not be placed anywhere in these folders or it may not (probably won't) work.

.NET Framework 4.7.2 or above is required. All modern versions of Windows should come with this already.

Building

  • Use Visual Studio 2022, .NET Framework 4.7.2 targeting, also going to need the C++ workload installed.
  • Use Release_Public for the standard AngelLoader build.
  • All dependencies now are either NuGet packages or are included in the bin_dependencies folder, so you should be able to just build with no fuss now.

License

AngelLoader's code is released under the MIT license, except portions which are otherwise specified. AngelLoader contains portions of code from the following:

  • Ookii Dialogs by Sven Groot, modified by Caio Proiete, and further modified and slimmed down by myself. This code is released under the BSD 3-clause license.
  • DarkUI by Robin Perris
  • FFmpeg.NET by Tobias Haimerl
  • SharpCompress (stripped down to the bare minimum for 7z archive entry enumeration) by Adam Hathcock

FMScanner, which has now been forked to be part of AngelLoader, uses portions of code from the following:

angelloader's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

maxigaz rostok

angelloader's Issues

Improve performance of UI initialization

RMGUIs of course have a ton of objects storing a bunch of redundant state, and layout performance is an issue as well (and it's done by the framework, of course). Still, we can do some things to speed it up. One thing we can do is to defer loading of UI elements that aren't visible immediately. I've already started on doing this with a couple of context menus. We can also try to do as much language setting as possible before the form shows, to try and avoid duplicate layouting. If we wanted to be hardcore we could even have two InitializeComponents() paths, one for debug which is the designer-generated version, and another that we generate ourselves that removes all pre-set settings that we're going to change anyway, giving us fine control.

Deal with excessive game tab horizontal space-taking

If we add SS2, then the space our game tabs take up will be starting to get a little bit hoggy. We could do either or both of the following:

  1. Add option to only show game tabs for games whose exes have been specified.
  2. Add option to collapse game tabs to just their icons. Either individually or as an all-or-nothing option (the latter would be easier).

Exceptions that matter should be reported (fail-early)

We have far too many places where we just swallow and log exceptions and then move on like nothing happened. We should stop kidding ourselves and inform the user of important errors.

  • Core
    • Init()
    • OpenSettings()
    • SetGameDataFromDisk()
    • RefreshFMsListFromDisk()
    • AddDML()
    • RemoveDML()
    • GetDMLFiles()
    • OpenFMFolder()
    • OpenWebSearchUrl()
    • ViewHTMLReadme()
    • OpenHelpFile()
    • OpenLink()
    • EnvironmentExitDoShutdownTasks()
    • DoShutdownTasks()
    • Shutdown()
  • FindFMs
  • Scanner
  • Game config files
  • FMData.ini
  • Config.ini
  • Install/uninstall/play
    • Pre-checks before install and uninstall
    • Failed installs (error dialog and cleanup)
      • Failed cleanup of failed or canceled install
    • Failed generation of missing missflag.str
    • Failed to write stub comm file
    • Failed to start exe
    • Check for used .mis files
    • Check if OldDark
    • Check if palette fix required
  • Backup/restore
    • Backup
    • Restore
  • Cache
  • Import
  • Audio conversion
  • FMLanguages
  • FFmpeg.NET.Engine
  • Settings window
  • Mods list read fail
  • FM languages read fail

[add others here]

Better help (integrated, context-sensitive)

We have a manual, but probably a lot of people aren't going to want to slog through that. We should at the very least have context-sensitive help that links out the relevant section in the manual, or ideally break it up into wiki-like sections or something.

Code for 7-zip performance characteristics

7z files are usually solid archives, so it's very slow to do anything with them (it's prohibitively slow to grab individual files; extracting the whole archive to disk is also slow but much faster on average).

Two things need doing:

  1. When scanning individual FMs, and the scan is taking longer than half a second or so, we should put up a progress box so the user knows something is happening.
  2. When extracting 7z data to cache, we currently do the same as zip files and grab the files individually. We should instead extract the archive fully to a temp folder and then copy files from there, as it will be much faster. We should also put up a similar progress box here.

AngelLoader.Common.GlobalCatOrTag appearing in tags list

I've stumbled across a small bug:

  • Add a new namespace and tag to an FM, for instance, bug:yes
  • Delete this new tag
  • Press Add from list

Doing so will cause the text AngelLoader.Common.GlobalCatOrTag to fill this position in the tags list instead of being removed.

fm.ini

Is is possible for angelload to write back the fm.ini files with the contents of angeloader's DB?

Optional auto-update functionality

Not sure how many people keep up with new versions, but currently you have to be checking the thread (or subscribed to it), then manually download and extract. If you want other languages, you have to manually download and extract those too. Auto-update would make the whole process easy and pleasant. Of course the user could turn it off and it would always ask permission first even if it was on. Don't wanna be dicks like Microsoft.

Switch stub to C++

We want to switch AngelLoader_Stub to C++ for the following rationale:

We currently use 3F/DLLExport to allow the current .NET 4.7.2 stub to be called into by Thief. However, DLLExport doesn't support the newer SDK-style project format, which .NET Core 3 requires. If we wanted to keep the project in C#, we have two options:

  1. We could leave the project as-is, targeting Framework, and require that users have both Framework 4.7.2 and Core 3 installed. This feels sloppy and presents a slightly unprofessional-looking picture to end users
  2. We could ditch DLLExports and do the ildasm/ilasm export add process manually by ourselves - it's actually pretty simple and I've done it manually before. However, this requires somehow including - or having an automated way to download - the ildasm/ilasm executables in our source distribution, which I'm not too keen on either.

However, with Core 3 now including the option for ReadyToRun where native code gets added into the compiled exe/dll - and therefore the ildasm/ilasm method is not supported at all - I'm starting to think this whole hack is a little shaky and who knows what'll happen in the future. Porting the stub to C++ allows it to work seamlessly with the NewDark call-in as it was meant to, and avoids any possible future problems. Since the stub is completely standalone, connected to the main app only through a small text-based temp file, and since it's extremely small and simple, even a C++ newb like myself can port it.

Support DromEd?

DL and NDL seem to be able to do this, so I guess I should too.

We could be more courteous as regards other loaders

Right now we kinda stomp on peoples' config files to set AngelLoader_Stub.dll as the FM selector. That's sort of fine because every loader needs to have itself specified in the config file to work, but due to our standalone nature, we're not necessarily as obvious about it as a conventional loader would be. Because other loaders are contained within the actual .dll, if you start "thief.exe -fm" expecting FMSel to start, but you get NewDarkLoader, well, at least you know that NewDarkLoader got set as the selector somehow. But with us, if you say "thief.exe -fm" and we're the loader, then the stub dll will start, but it doesn't do anything visual, it just looks for data in the stub comm file in our temp folder. If it finds it, it loads the FM specified; if it doesn't or it's invalid, then it just loads the normal game without an FM and doesn't tell the user or do anything that would make it clear what's going on. See this thread. We could maybe make it so the stub ALWAYS needs the comm file in order to start the game silently, and we can just specify whether to play the original game or an FM. Then, if it doesn't find a comm file, it could maybe throw up a dialog box and explain itself at least. Or do something else smarter, like give the option to restore other loaders?

Asynchronously load RichTextBox contents

Loading large and/or image-heavy .rtf files into a RichTextBox can be horrendously slow (see An Enigmatic Treasure With A Recondite Discovery's 10MB .rtf readme with multiple huge images). This can make for a very poor user experience on startup as the form struggles to draw itself while the RichTextBox blocks and hogs the thread. We'd really like to be able to simply load its content in a separate thread and await it, but because the RTB is part of the UI, it's flat-out impossible to thread it.

EDIT: Nope, we're not going to any of the below. Instead we're going to do something way easier and more sane. As always, I research till my brains fall out and the only solution I can find is a disgusting hack, then as soon as I resign myself to actually doing said hack, a totally elegant, sane, and nice solution presents itself. Why can't these things just pop up earlier. Ah well.

Therefore, to achieve asynchrony, we're going to run it as its own process and host it inside the main UI so it will look like it's a normal part of it. We're still going to have to pop it off the UI to do the load and then pop it back on (because even though it's a separate process that's being hosted, if it's displayed on the UI, it blocks), but that's not a big deal really.

We can slap in a nice loading spinner or something while it loads. This way, the form will come up and draw itself fully immediately, and only the readme may lag behind (but it won't block and will inform the user it's loading).

Realtime UI for translators

This was suggested in the TTLG thread a while ago. By pulling apart the UI and converting it to passive-view (with an actually good reason other than coding dogma, yay!) and then sticking it in a separate .dll, I could make a translation app that uses the UI so translators can see their translations in realtime and without having to sometimes dig deep into functionality that might be destructive or that they might not be able to see. We could allow the translator to see every section of the UI harmlessly (example: uninstalling FMs, converting audio files, viewing uncommon error scenarios that might be difficult or impossible to make happen in the regular app, etc).

Dark mode

Dark Mode TODOs:

FMsDGV:

  • Darken background of FMsDGV
  • Change FMsDGV selection color from standard blue to something pleasanter looking
  • Finish different-color border painting of recent FM cells
  • Decide what to do about the blue highlights in the headers - ownerdraw classic mode and remove them?

Images:

  • Darken zoom images
  • Darken refresh image
  • Darken game images
  • Darken installed image
  • Darken red question/exclamation images
  • Darken stars/rating images
  • Darken install/uninstall images (doesn't need it)
  • Darken filter images:
    • Release date
    • Last played
    • Tags
    • Finished / Unfinished
    • Rating
    • Show unsupported (doesn't need it)
    • Show unavailable (doesn't need it)
    • Show recent at top
  • Darken trash-can image
  • Darken filter bar right-side images
  • Darken "AL Blue" images (web search, magnifier icons)
  • Darken all other images that need it

Controls

  • Darken date filter windows
  • Darken ListBox (DGV has the auto-select problem which is a bad idea to keep working with, so try a custom ListView)
  • Darken AddTagDropDown (have another look at deriving it from a real drop-down, now that I'm more experienced in custom crap)
  • Darken ChooseReadmePanel stuff
  • Darken NumericUpDown
  • Darken GroupBox
  • Darken RadioButton
  • Darken TreeView (plus and minus aren't dark, but who cares, it looks fine enough)
  • Darken game tabs
  • Darken DateTimePicker
  • Darken MessageBoxCustom
  • Darken scroll bars
  • Darken normal MessageBoxes
  • Darken Ookii dialogs
  • Darken ToolTips
  • Darken ProgressBar

ToolStrip:

  • Darken painted tool strip separators
  • Make dark ToolStripButtons have an appropriate selection/border/pressed color scheme

Colors:

  • Sort out colors: make a final set of colors and get rid of all the ones we don't need
  • Go through all Color / Brush / SolidBrush / Pens and find any straggling colors unaccounted for in dark mode (eg. the red rating text on the filter bar when you set a rating filter)
  • Get rid of using/dispose on brushes and pens and just make them all static single-instance per color

Misc:

  • Address all @DarkMode todos in the code
  • Bring DarkUI into AngelLoader project and de-dupe all the stuff
  • Make DarkTextBox keep its scroll position on mode change
  • ReSharper cleanup spree

Steam Support

AngelLoader already works fine with the steam version of Thief 2 (the only one I tried, I expect T1 and DS to work fine too), the only issue is it bypasses Steam itself when running the .exe

Would be great to add support for launching the game through Steam so one gets the Steam overlay, screenshots and playtime functionality while in FMs.

I could attempt to add this myself and file a PR if you think it's something you'd like added now and see a way it could be integrated in the GUI (a Use Steam checkbox?)

I'm guessing the best bet would be to use steam.exe -applaunch <appID> [launch parameters] in the backend.

Better drop-downs for filter buttons

Currently, we have a few filter buttons that require drop-downs so the user can enter more information (these are the two date filter buttons and the rating filter button). Right now, these drop-downs are just separate windows with OK and Cancel buttons. This is functional enough but looks hacky, and autosizing based on localized strings (particularly the title bar string) is difficult (and isn't currently done). At this point we have a global mouse hook system in place, so we can dig out the old custom drop-down tests and convert them to use the global mouse hook so they can function correctly and without glitches or corner cases. We then won't have or need a title bar at all, and we can get rid of the side-by-side OK and Cancel buttons too, and then autosizing will be easy.

  • Implement autosize-width
  • Make windows into some kind of dropdowns instead

Try making a .NET 5.0 port and test on Wine

Apparently .NET 5 works well on Wine, so we could try this.

I'm not going to get my hopes up that it won't continue to be an order of magnitude slower than Framework 4.7.2, but at least it should work much better on Linux.

Full diff backup / restore like FMSel

This feature is just too good to ignore, at least for me - I've always wanted to be able to easily keep modifications to mission folders like adding an fm.cfg to add bloom for just that mission, or turn off new mantling for OldDark missions (to prevent me being uncertain if I was meant to be able to mantle somewhere), fixing readables, etc. It would also make us more compatible with the setups of folks who use FMSel with this option.

Additionally, it would allow for all kinds of crazy cool FM-specific options in the future - we could have simple checkboxes that could turn things on and off per-FM (bloom etc.) and they would be permanent.

Make the column highlighting useful or get rid of it

It's an automatically enabled accessibility thing. We can turn it off by turning off "level 2 accessibility features" (or some similar name) but I don't want to do that because who knows what else it might turn off that somebody might be using. It's incredibly stubborn and can't be disabled otherwise (not even with reflection afaik), nor can it even be tracked programmatically. If we could track it, then we could do as Dahenjo suggested and use it to allow keyboard column sorting by detecting which column is highlighted and sorting that column when the user presses eg. Ctrl-up or Ctrl-down.

With .NET Core 3, the WinForms source code is part of it and is MIT licensed like everything else, so we can do like we did with the zip stuff and just grab the DataGridView code, modify it, and there you go. We can either use the highlighting to track and allow keyboard nav, or just get rid of it. ETA November

Some way to show or highlight newly added missions

In testing with my enormous list of FMs, newly downloaded ones are just thrown in the mix and I have to remember their name and filter for them... some way to highlight newly added missions or something would be good

Suggestion: Option to save backup files next to FM files

I've just tried out AngelLoader v1.4, and noticed that after setting the backup path in the options, I can’t unset it. Also, as I can see, AngelLoader can load backup files in the FM archives (saved by fmsel).

Would it be possible to allow the user to save the backup there, next to the FM files, as well?

Write some documentation

I would hope it's mostly pretty easy to get the hang of, but it's never a bad thing to explain everything explicitly (and there are a few corners)

Layout and draw the entire window before showing it

Like it used to be. I changed that a while back to make it easier to run the game type scanner on startup if need be, but it'd be better to have the whole window ready (except possibly the readme of course, but maybe even that?) by the time you see it.

Match FMSel behavior regarding languages

FMSel does some stuff with detecting an FM's supported languages that we don't do. We should match its behavior, otherwise we're technically not to spec, as it were.

Option overrides

Add per-FM and per-game "option overrides" (pass config vars to the game).

  • Add per-FM mantle mode
  • Add "use old mantle for all OldDark missions" option
  • Add per-game mantle mode
  • Decide if we want to support other stuff like "screenshot mode" or the "d3d_disp_sw_cc" gamma fix thing etc.

--- OLD TEXT: ---

When I used to do videos, I would often find it necessary to change certain options temporarily in various config files ("inv_status_height 0" in user.cfg for hiding the HUD for screenshots, "force_windowed 1" in cam_ext.cfg when I was streaming, etc.). This was always a pain. We should have an option to run the game with a set of custom options and provide some checkboxes and an additional text field for writing in custom commands. Some options can be passed on the command-line; others will need to have their appropriate files temporarily changed. That will require us to track the running of games, rather than just fire-and-forget like we do now.

SS2 support

It means I have to research a game I really have no interest in and would rather not have to get down and dirty with... but can we really call ourselves a "DarkLoader successor" if we don't?

Customize sorting for each column individually

While working on improving the sorting behavior of the FMs list columns, I realized that there could be multiple different ways you might want to sort a column. For instance, for the rating column, it could be much nicer to sort only the rated rows, and leave all unrated rows at the bottom at all times. That way you don't have to get a sea of empty rows at the top and then scroll all the way down to see your ratings. But then again, it could also sometimes be desirable to have that behavior if you're interested in seeing which FMs you haven't yet rated in particular. Or, as in the Last Played column, you might want to sort in reverse first because it makes more sense to have the most recent times at the top, but then again sorting normally is "more logical". We could have a Settings section where you can set these kind of options per-column maybe. We could also add a submenu to the column header context menu where you could sort a certain way just that once.

Corner case: Steam and the stub

Possible stub comm file not being deleted in the following scenario:
You launch a game through Steam, but the game doesn't actually launch (because you don't have it in your Steam library or any other situation in which it gets cancelled). Because the game never runs, it never deletes the stub comm file. The next time the game runs, it finds the stub file and loads up whatever FM was specified. This won't happen if you launch an FM or original game from AngelLoader, as we delete or overwrite the stub file ourselves before playing anything, but if you were to run the game manually, it would load whatever FM was specified in the stub once, and then delete the stub file, so if you ran it again it would properly start the original game and everything would be fine again.

I could solve it if there was a way to detect if we were being launched through Steam (in C++). I don't know if there is, but then I could just specify a Steam=True line in the stub file, and then if we're being launched through steam we read and act on it as usual, but if we're not, then we just delete it and ignore.

I'll have to buy the games on Steam to test this. Or just buy one so I can have one game that works and one that doesn't, so I can test both paths.

Redesign Settings window

If we redesign it to be more vertical with a list of sections on the left and then an autosizing single-column design on the right for the actual settings (all within a resizable window), it would make it easier and more natural to support variable-width localized strings without the awkward kludges we have to do to accommodate them currently. For instance I notice that even with my allowance for two lines of text, the German translation for "Ignore the following leading articles when sorting by title:" still takes up the entirety of both lines. We want to change the design to fix this before we run past an actual limit.

Question: String for date format options in English.ini

As I was translating English.ini, I noticed that two of the strings say Current culture short and Current culture long, appearing in the Options. Wouldn’t it be better to display System settings, short? (Maybe it’s just me, but I think it sounds clearer this way.)

Also, there’s a string for an error that says, The date and time is outside the range of dates supported by the calendar used by the current culture. When does this normally appear? Does this appear when an FM contains a release date like “the 33rd of December”? (First, I thought it has to do with the custom format you can set in the Options, but using the drop-downs, no matter what I set, it all appears fine without any errors.)

Allow multiple FM selection

This would need some reasonably serious refactoring of the guts, but I notice it can get really annoying and tedious if you want to do stuff to a bunch of FMs, like say mark off finished-on state etc. With multiple FM selection, we could just click "Expert" and they all get marked. We could even install or uninstall multiple FMs at once.

Multiple selection support progress:

  • Localizable strings finalization, making sure there's plural messages for the appropriate things, etc.
  • Pin/unpin
  • Rating change
  • Finished state change
  • FM installation
    • Allow canceling in the middle of restore and audio convert (for this code path only) for more responsiveness
  • FM uninstallation
    • Basic functioning
    • Add stop button / feature
    • Test error and edge cases
    • Make sure it works with Delete when user chooses to uninstall also
  • FM audio conversion (manual)
    • Checks and ignoring inapplicable
    • Conversion process
    • Cancel / stop
  • Free space checking
    • Before install itself
    • Backup restore on install
    • We can't really know backup size, because our backups will be compressed and we won't know how big they'll be until we actually compress them...
    • Would like audio conversion, but can't because we can't know in advance how big the converted files will be (or can we? Since we're always converting to 16-bit wavs, maybe we can get the length/bitrate and calculate?)
  • Scan selected FMs
  • Delete FMs
    • Handle uninstall
    • Handle error/edge cases for multiple
  • Fix DGV keynav to work like before (regression)
    • List should scroll to top/bottom on "go to top or bottom" command (home/end/ctrl+up/ctrl+down)
    • Selection should follow top/bottom command (all FMs from current to top/bottom should be selected)
    • Don't reload FM if we're at top/bottom and hit against the top/bottom
    • If we're on top/bottom, a non-shift press of a direction against the top/bottom should deselect other FMs and leave only the one closest to the edge selected
    • Do reload top/bottom FM in the above case if the previously displayed FM is not the top/bottom one
    • Selection reversing/shrinking needs to work when the selection is touching the top/bottom
    • Okay there's a couple corner cases where it's not 100% exactly the polished behavior (shift-home/end reversing selection toward edge doesn't work, etc.) but meh :\

Full and proper support for high DPI

Right now, we just let Windows scale us as it sees fit, with the result that our UI looks slightly blurry when not at 100% scale, and I have reports that fonts may not be scaling quite as they should either. We might have to switch to .NET Framework 4.8 to do this, as judging from the release notes there's some high-DPI support improvements for WinForms in there that we might need. However, the requirements are the same as for 4.7.2, so it's not that much of a problem, other than making people download an entire new very large framework version that they may not have.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.