Full Code of fieryhenry/BCSFE-Python for AI

main 93355c12b931 cached
208 files
1.1 MB
297.3k tokens
2892 symbols
1 requests
Download .txt
Showing preview only (1,268K chars total). Download the full file or copy to clipboard to get everything.
Repository: fieryhenry/BCSFE-Python
Branch: main
Commit: 93355c12b931
Files: 208
Total size: 1.1 MB

Directory structure:
gitextract_nh3e0iid/

├── CHANGELOG.md
├── LICENSE
├── LOCALIZATION.md
├── MANIFEST.in
├── README.md
├── pyproject.toml
├── requirements.txt
├── setup.py
├── src/
│   └── bcsfe/
│       ├── __init__.py
│       ├── __main__.py
│       ├── cli/
│       │   ├── __init__.py
│       │   ├── color.py
│       │   ├── dialog_creator.py
│       │   ├── edits/
│       │   │   ├── __init__.py
│       │   │   ├── aku_realm.py
│       │   │   ├── basic_items.py
│       │   │   ├── cat_editor.py
│       │   │   ├── clear_tutorial.py
│       │   │   ├── enemy_editor.py
│       │   │   ├── event_tickets.py
│       │   │   ├── fixes.py
│       │   │   ├── map.py
│       │   │   ├── max_all.py
│       │   │   ├── rare_ticket_trade.py
│       │   │   └── storage.py
│       │   ├── feature_handler.py
│       │   ├── file_dialog.py
│       │   ├── main.py
│       │   ├── recent_saves.py
│       │   ├── save_management.py
│       │   └── server_cli.py
│       ├── core/
│       │   ├── __init__.py
│       │   ├── country_code.py
│       │   ├── crypto.py
│       │   ├── game/
│       │   │   ├── __init__.py
│       │   │   ├── battle/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── battle_items.py
│       │   │   │   ├── cleared_slots.py
│       │   │   │   ├── enemy.py
│       │   │   │   └── slots.py
│       │   │   ├── catbase/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── beacon_base.py
│       │   │   │   ├── cat.py
│       │   │   │   ├── drop_chara.py
│       │   │   │   ├── gambling.py
│       │   │   │   ├── gatya.py
│       │   │   │   ├── gatya_item.py
│       │   │   │   ├── item_pack.py
│       │   │   │   ├── login_bonuses.py
│       │   │   │   ├── matatabi.py
│       │   │   │   ├── medals.py
│       │   │   │   ├── mission.py
│       │   │   │   ├── my_sale.py
│       │   │   │   ├── nyanko_club.py
│       │   │   │   ├── officer_pass.py
│       │   │   │   ├── playtime.py
│       │   │   │   ├── powerup.py
│       │   │   │   ├── scheme_items.py
│       │   │   │   ├── special_skill.py
│       │   │   │   ├── stamp.py
│       │   │   │   ├── talent_orbs.py
│       │   │   │   ├── unlock_popups.py
│       │   │   │   ├── upgrade.py
│       │   │   │   └── user_rank_rewards.py
│       │   │   ├── gamoto/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── base_materials.py
│       │   │   │   ├── cat_shrine.py
│       │   │   │   ├── catamins.py
│       │   │   │   ├── gamatoto.py
│       │   │   │   └── ototo.py
│       │   │   ├── localizable.py
│       │   │   └── map/
│       │   │       ├── __init__.py
│       │   │       ├── aku.py
│       │   │       ├── challenge.py
│       │   │       ├── chapters.py
│       │   │       ├── dojo.py
│       │   │       ├── enigma.py
│       │   │       ├── event.py
│       │   │       ├── ex_stage.py
│       │   │       ├── gauntlets.py
│       │   │       ├── item_reward_stage.py
│       │   │       ├── legend_quest.py
│       │   │       ├── map_names.py
│       │   │       ├── map_option.py
│       │   │       ├── map_reset.py
│       │   │       ├── outbreaks.py
│       │   │       ├── story.py
│       │   │       ├── timed_score.py
│       │   │       ├── tower.py
│       │   │       ├── uncanny.py
│       │   │       └── zero_legends.py
│       │   ├── game_version.py
│       │   ├── io/
│       │   │   ├── __init__.py
│       │   │   ├── adb_handler.py
│       │   │   ├── bc_csv.py
│       │   │   ├── command.py
│       │   │   ├── config.py
│       │   │   ├── data.py
│       │   │   ├── git_handler.py
│       │   │   ├── json_file.py
│       │   │   ├── path.py
│       │   │   ├── root_handler.py
│       │   │   ├── save.py
│       │   │   ├── thread_helper.py
│       │   │   ├── waydroid.py
│       │   │   └── yaml.py
│       │   ├── locale_handler.py
│       │   ├── log.py
│       │   ├── max_value_helper.py
│       │   ├── server/
│       │   │   ├── __init__.py
│       │   │   ├── client_info.py
│       │   │   ├── event_data.py
│       │   │   ├── game_data_getter.py
│       │   │   ├── headers.py
│       │   │   ├── managed_item.py
│       │   │   ├── request.py
│       │   │   ├── server_handler.py
│       │   │   └── updater.py
│       │   └── theme_handler.py
│       ├── files/
│       │   ├── locales/
│       │   │   ├── en/
│       │   │   │   ├── core/
│       │   │   │   │   ├── config.properties
│       │   │   │   │   ├── files.properties
│       │   │   │   │   ├── input.properties
│       │   │   │   │   ├── locale.properties
│       │   │   │   │   ├── main.properties
│       │   │   │   │   ├── save.properties
│       │   │   │   │   ├── server.properties
│       │   │   │   │   ├── theme.properties
│       │   │   │   │   └── updater.properties
│       │   │   │   └── edits/
│       │   │   │       ├── bannable_items.properties
│       │   │   │       ├── cats.properties
│       │   │   │       ├── enemy.properties
│       │   │   │       ├── fixes.properties
│       │   │   │       ├── gambling.properties
│       │   │   │       ├── gamototo.properties
│       │   │   │       ├── gatya.properties
│       │   │   │       ├── gold_pass.properties
│       │   │   │       ├── items.properties
│       │   │   │       ├── map.properties
│       │   │   │       ├── medals.properties
│       │   │   │       ├── missions.properties
│       │   │   │       ├── playtime.properties
│       │   │   │       ├── scheme_items.properties
│       │   │   │       ├── special_skills.properties
│       │   │   │       ├── talent_orbs.properties
│       │   │   │       ├── treasures.properties
│       │   │   │       └── user_rank.properties
│       │   │   ├── tw/
│       │   │   │   ├── core/
│       │   │   │   │   ├── config.properties
│       │   │   │   │   ├── files.properties
│       │   │   │   │   ├── input.properties
│       │   │   │   │   ├── locale.properties
│       │   │   │   │   ├── main.properties
│       │   │   │   │   ├── save.properties
│       │   │   │   │   ├── server.properties
│       │   │   │   │   ├── theme.properties
│       │   │   │   │   └── updater.properties
│       │   │   │   ├── edits/
│       │   │   │   │   ├── bannable_items.properties
│       │   │   │   │   ├── cats.properties
│       │   │   │   │   ├── enemy.properties
│       │   │   │   │   ├── fixes.properties
│       │   │   │   │   ├── gambling.properties
│       │   │   │   │   ├── gamototo.properties
│       │   │   │   │   ├── gatya.properties
│       │   │   │   │   ├── gold_pass.properties
│       │   │   │   │   ├── items.properties
│       │   │   │   │   ├── map.properties
│       │   │   │   │   ├── medals.properties
│       │   │   │   │   ├── missions.properties
│       │   │   │   │   ├── playtime.properties
│       │   │   │   │   ├── scheme_items.properties
│       │   │   │   │   ├── special_skills.properties
│       │   │   │   │   ├── talent_orbs.properties
│       │   │   │   │   ├── treasures.properties
│       │   │   │   │   └── user_rank.properties
│       │   │   │   └── metadata.json
│       │   │   └── vi/
│       │   │       ├── core/
│       │   │       │   ├── config.properties
│       │   │       │   ├── files.properties
│       │   │       │   ├── input.properties
│       │   │       │   ├── locale.properties
│       │   │       │   ├── main.properties
│       │   │       │   ├── save.properties
│       │   │       │   ├── server.properties
│       │   │       │   ├── theme.properties
│       │   │       │   └── updater.properties
│       │   │       ├── edits/
│       │   │       │   ├── bannable_items.properties
│       │   │       │   ├── cats.properties
│       │   │       │   ├── enemy.properties
│       │   │       │   ├── fixes.properties
│       │   │       │   ├── gambling.properties
│       │   │       │   ├── gamototo.properties
│       │   │       │   ├── gatya.properties
│       │   │       │   ├── gold_pass.properties
│       │   │       │   ├── items.properties
│       │   │       │   ├── map.properties
│       │   │       │   ├── medals.properties
│       │   │       │   ├── missions.properties
│       │   │       │   ├── playtime.properties
│       │   │       │   ├── scheme_items.properties
│       │   │       │   ├── special_skills.properties
│       │   │       │   ├── talent_orbs.properties
│       │   │       │   ├── treasures.properties
│       │   │       │   └── user_rank.properties
│       │   │       └── metadata.json
│       │   ├── max_values.json
│       │   └── themes/
│       │       ├── default.json
│       │       └── discord.json
│       └── py.typed
└── tests/
    ├── __init__.py
    └── test_parse.py

================================================
FILE CONTENTS
================================================

================================================
FILE: CHANGELOG.md
================================================
# Changelog

## [3.3.0] - 2026-04-01

### Added

- Display basic save info when a save is loaded (region, version, partial inquiry code)

- Prompt to ask if you want to change game data repo to gitlab if battlecatsmodding.org is not loading

### Fixed

- JP 15.3.0 save parsing

- Version checking for version numbers >= 10

- Talent orb effect colours being broken on some terminals

- Maybe fixed cat shrine not appearing when using the feature to make it appear

- Some issues when reading/writing invalid cat talents

- Editor crashing when inputing an option too high if disable maxes is enabled

## [3.2.2] - 2025-12-29

### Added

- Options within the cat shrine feature to make it appear/disappear in-game

### Changed

- Reworked how clearing maps/stages works for non story chapters - setting map progress and clear
count are now done separately.

### Fixed

- Fixed a few more issues with game data downloading

- Fixed some issues where disabling max values didn't work for some features

- Fixed unknwon map names sometimes showing as blank rather than "Unknown Map Name"

- Improved speed of getting map names for most map clearing features

- Special skill upgrading crashing if you disable max values and you enter an upgrade value which is too large

- Game data repo config value URL is validated when trying to change it

## [3.2.1] - 2025-11-23

### Added

- Feature to add endless battle items

### Fixed

- Fixed game data downloading when upgrading from a previous editor version

- Fixed disable max values not working in some situations

- Fixed an issue where maps not present in the save file could be selected, causing an error

## [3.2.0] - 2025-11-21

### Added

- Feature to edit the cat storage

- Feature to reset the golden cat cpu uses

- Feature to clear enigma stages

- Feature to clear catclaw championships

- Feature to reset cat scratcher and wildcat slots

- Cats of the Cosmos Chapter 3 outbreaks

- Features to push to root storage and rerun game

- Option to select cats by the game version they were released in

### Fixed

- Fixed issue of not being able to add enigma stages if they have already been cleared

- Fixed issue on some platforms where it couldn't find JSONDecodeError

- Fixed a few text and input issues

- Fixed issue where editing event tickets could cause your inquiry code to change

### Changed

- Game data repo is now at <https://git.battlecatsmodding.org/fieryhenry/BCData>

- Game data is now downloaded and saved all at once, rather than file by file which should speed up some features

- When running the Update External Content feature, it asks you if you want to clear the current game data

## [3.1.0] - 2025-09-03

### Added

- Added a way to choose a gacha banner by id rather than by name

- Feature to clear catamin stages and set the clear times to whatever you want

- When selecting cats you can now filter down your selection, e.g Select current rare cats from
  this specific gacha banner

- Better error message if your device is not rooted when trying to pull from root storage

- Better error message when failing to create a config file


### Changed

- Changed default game data repo from <https://github.com/fieryhenry/BCData> to <https://git.fyhenry.uk/BCData>

- Changed a few other urls to point to the Codeberg repo rather than the GitHub repo

- Increased speed of downloading all cat names

- Made a few more option selections work if you enter the text of the option rather than the
  number associated with it


### Fixed

- Fixed talent parsing issue for some save files

- Fixed gacha banner selection using old banners rather than newer ones for banners which have the
  same name

- Disable maxes config option now works for upgrading cats

- Fixed regression where editing scheme items raised an exception

- Increased default timeout for requests and made some requests have no timeout to fix issues with
  slow internet connection

- Issue where an exception would be raised if the editor couldn't auto-detect your country code
  when downloading save data


## [3.0.1] - 2025-08-04

### Fixed

- Installing the editor on certain platforms due to a dependency issue

## [3.0.0] - 2025-08-04

This is a full re-write of the editor, so many things were added, changed and fixed, and I didn't
really document the changes that well, so here's a summary:

### Added

- Game Data for es, it, de, th, fr locales and a way to change what repo to use for game data

- Better color and localization support

- Better adb usage

- Waydroid support

- More config options

- Lucky tickets

- Treasure Chests

- Support for new talent orbs

- Ultra Form Support

- Better save backups

_ More support for older game versions

- Labyrinth medals

- Many more things

### Changed

- Improved the wording on a few features

- Cats will be auto-unlocked by default when upgrading / true forming, etc

- Many more things

## [2.7.2.3] - 2023-06-19

### Fixed

- New version of colored crashing the editor by forcing the editor to use the
old version (new colored version renamed stuff and also raises an exception when
not using a specific set of colors)

- Max value for equip slots being too high, for some reason ponos has allocated
space for 18 equip slots but has only allocated space for 17 slot names

## [2.7.2.2] - 2023-06-08

### Fixed

- The editor crashing when editing meow medals or event stages

## [2.7.2.1] - 2023-05-28

### Fixed

- The editor crashing if user info not found

## [2.7.2] - 2023-05-28

### Added

- Ultra Talent Support

- 12.2.0 cannon support

### Changed

- Improved item tracking and user info tracking so your inquiry code shouldn't
change as much

### Fixed

- Issues with the max values for some multi items

- Disable maxes config option not being checked

- Gold pass not being able to be removed

## [2.7.1] - 2023-03-22

### Added

- A feature to convert save versions e.g en to jp - might give issues and only
works if both apps are the same version

### Changed

- Base material names are no longer hardcoded and so jp base material names exist
now

- New cats no longer cause the cat capsule machine still thinking the cat is new

- Talent orbs editing works better now + aku orbs

### Fixed

- Things like treasures and gold pass id crashing the editor when entering too
large of a number

- Jp 12.2.0 save parsing

- The first mission not showing up in mission clearing

- Gatya seed not being able to set above the 32 signed int limit

- Outbreaks crashing

## [2.7.0] - 2023-01-08

### Added

- Features to clear legend quest, behemoth culling stages, and collab gauntlets

- Feature to get scheme item rewards (e.g go go pogo cat mission rewards)

- More support for rooted android devices (pull and push directly to root
folder + re-run game)

- The ability to remove talents

- The ability to select / download a new save without having to restart the editor

### Changed

- Catseye editing will now use the game data for names - means i don't need to
update the whole editor to put another catseye type in

- When uploading the managed items, a save key is added (idk if this changes
anything / reduces bans but newer game versions do this)

- The editor will never ask if you want to exit, to exit enter the option to exit
or do `ctrl+c`

- Renamed feature `Create a new account` to `Generate a new inquiry code and token`
to better reflect what it does

### Fixed

- Cat name selection for jp

- Evolve cats and upgrade cats crashing if game data is outdated

- Main story crashing and chapter names being offset sometimes

- Dojo score not being able to be edited if you haven't been to the dojo yet

- Max value for some items being an unsigned int even though the game reads
signed ints

- Outbreak clearing not setting all stages

- Jp timed score rewards being parsed and serialized incorrectly leading to
incorrect timed scores being edited in

### Removed

- The `pick` module due to issues with python 3.11

## [2.6.0] - 2022-10-24

### Added

- Editor support for android. Using termux you can now run and install the editor

- On crash, the editor will ask if you want to save your changes and upload
managed item changes to the servers

- A way to remove meow medals

- A feature to play the CotC 3 filibuster stage again

- A way to remove outbreaks

- A feature to unlock the aku realm

### Changed

- When upgrading cats, if you upgrade past the normal max for that cat then the
level cap of the cat will also increase / decrease to match. (E.g if you upgrade
a cat to level 35 using the editor, then use a catseye in game then it will
unlock level 36 instead of level 31)

- How selecting stages to clear works. Instead of selecting stage ids you enter
a stage to complete the progress to (e.g entering 5 clears the first 5 stages,
and entering 48 clears them all and then if you then enter 5 again it will clear
the level progress for the levels 6-48)

### Fixed

- Crash if using an older game version and getting cats by rarity / gatya id

- Talents crashing

- CotC 2 and 3 appearing in the outbreaks feature when they don't have outbreaks

- The editor crashing if you don't have an internet connection

## [2.5.0] - 2022-10-14

### Added

- A feature to fix time related issues (HGT, no energy recovery, etc)

### Changed

- Features that fix things (fix time related issues, fix gamatoto crashing the
game, fix equip menu not unlocked, etc) have been moved / copied to their own
category called `Fixes`

### Fixed

- Having a very high playtime not allowing you to transfer

- Having corrupted cat unlock flags messing up user rank calculation and not
letting you transfer

- Cat shrine not appearing when editing it

- Selecting cats from name not letting you select cats

- Transfer error messages not appearing in some cases

## [2.4.0] - 2022-10-05

### Added

- An option in save management to save the save data without opening the file
selection dialog

- Option to edit where the config file is located

- A way to enter an officer id or generate a random one when getting the gold
pass. Entering -1 for the officer id will remove the gold pass

### Changed

- Platinum shards max amounts now takes into account your current platinum ticket
amount to make sure you can't go over 9 tickets

- Made catshrine appear when using the edit catshrine level feature and the level
up dialogs are now skipped

- When pulling using adb the editor will automatically detect currently installed
game versions and let you select one to pull. If only 1 game version is installed
it will just default to that one.

### Fixed

- Selecting cats based on name crashing if entering a cat id too large

- Upgrade cats / special skills crashing the editor if setting the base level to
0 or a level to be larger than 65535

- Being unable to download a save / pull saves if your default country code is
longer than 2 characters. The editor will just ask you to manually enter it

- Treasure groups chapter selection ids being off by 1

## [2.3.0] - 2022-09-14

### Added

- Feature to add enigma stages

- Feature to edit Gamatoto shrine xp / level

- Replaced some unknown values in the save stats + updated parsing for 11.3.0
and up

### Changed

- Get gold pass will now give the paid version instead of the free trial and
each subsequent use of the feature will increase the total renewal times by 1
and wipe the daily catfood stamp count

### Fixed

- File not found error if item_tracker.json is not present

## [2.2.2] - 2022-09-04

### Added

- A new config option to select options with the arrow keys or j and k to select
some options. `EDITOR` -> `USE_ARROW_KEYS_FOR_FEATURE_SELECT`

### Fixed

- Default save path being empty, causing the editor to not be able to pull saves
unless changed

## [2.2.1] - 2022-09-04

### Fixed

- Editor sometimes crashing when saving a file when the file dialog

## [2.2.0] - 2022-09-03

### Added

- Option when selecting cats to only get obtainable cats (Only the cats that
show up in the cat guide)

- Option to select cats by name when selecting cats

### Changed

- Config file will now be located in the app data folder / home folder

- Character drop, evolve cats and talents will now be able to use the normal cat
selecting menu

- You can now select all chapters at once when editing treasure groups

### Fixed

- Wrong chapter being shown when selecting levels

- Editor crashing when entering the name of a category when selecting a feature

## [2.1.1] - 2022-08-17

### Changed

- Split up some features into subcategories e.g Treasures / Levels -> Treasures
-> Treasure groups. Or Items -> Tickets -> Normal Tickets

### Fixed

- Gamatoto helpers

## [2.1.0] - 2022-08-16

### Added

- The ability to unlock the equip menu

- The ability to upload catfood and other bannable item changes to the ponos
servers - this is done automatically whenever your save data is saved /
uploaded. This should in theory prevent bans from catfood and other items,
but it seems a bit unreliable so I've kept the warning in the editor

- A feature to claim all user rank rewards (Doesn't give any items)

- A way to select specific gacha banner cats - you need to go to the wiki for
the banner you want, and look at the name of the image e.g royal fest = 602

- The ability to get the gold pass

- A feature to create a new account - new iq and token

- A way to clear specific aku stages

- Some configuration options , e.g options to remove max limits, automatically
save changes after each edit, etc, the path to the config file is shown at the
top of the editor

### Changed

- You can now exit, catfood, rare, plat, and legend tickets after the warning is
shown

- The editor will now display "Press enter to exit" when exiting

- Whenever your inquiry code changes, the editor will upload your catfood and
other bannable item amounts to the servers - this should prevent bans

- When entering a transfer code, the editor will check for a hex number and when
entering a confirmation code it will check for a dec number. This should prevent
people confusing 0 for O

- Game data will now be downloaded from [here](https://github.com/fieryhenry/BCData)
when needed so that if I want to update the data in the editor, I don't have to
do a new release

### Fixed

- Select cats based on rarity being off by 1
- Evolve cats setting some cats to the first form

## [2.0.2] - 2022-07-08

### Fixed

- Jp not being able to upload save data

## [2.0.1] - 2022-07-04

### Fixed

- Upgrade cats and unlock event stages not working properly when editing all at once

## [2.0.0] - 2022-07-04

### Added

- The ability to upload your save data to the ponos servers and get transfer
and confirmation codes. (The editor's root requirement is now gone). Although,
you'll still need root access if you get banned / elsewhere popup. I haven't
tested the feature too much so it could lead to bans

- An option to go back in the feature menu

- An automatic updater, if there is a new update, it will ask if you want to
update and if you say yes then it'll try to update automatically

- A way to select `all` talent orbs to edit all at once

- A new tutorial video that shows you how to use the transfer system stuff and
unban an account [here](https://www.youtube.com/watch?v=Kr6VaLTXOSY)

### Changed

- The fix elsewhere / unban feature, it no longer needs another account.
You can still use the old one, now named `Old Fix elsewhere error / Unban
account (needs 2 save files)` if you want

- A bunch of the source code. You should now be able to import BCSFE_Python in
another python file and access the parser, serialiser, etc. Due to the rewrite,
some stuff may be broken. This, and testing, is where the majority of the time
went to

- The order of few options, to make the server stuff closer to the top as that's
what most people will be selecting now that no root is needed

### Fixed

- Some adb issues

- More save parsing issues

- Edit dojo score crashing if you haven't been to the dojo yet

- Adding adb to path issue

- Ototo cat cannon not setting the correct value when editing all at once

- Individual treasures feature giving you 49/48 treasures

## [1.8.0.1] - 2022-05-24

### Removed

- Import from a random module that got imported automatically by vscode

## [1.8.0] - 2022-05-24

### Added

- New behemoth stones to get catfruit feature
- The ability to fix gamatoto from crashing the game

### Fixed

- Some adb issues thanks to [!j0](https://github.com/j0912345)
- More save parsing issues

## [1.7.1] - 2022-05-20

### Fixed

- Save parsing issue with en 11.5

## [1.7.0] - 2022-05-20

### Added

- The ability to clear catnip challenges / missions
- The ability to complete cat cannons to certain stages (e.g foundation, style, cannon)
- The ability to set the Catclaw dojo score (only `Hall of Initiates` atm -
don't know if ranked stuff can be save edited)
- The ability to remove the `Clear "{stage_name}" for a chance to get the
Special unit {cat_name}` stage clear rewards when entering Legend Stages
- The ability to set the `maxed upgrades --> rare tickets` conversion thing to
allow for unbannable rare tickets to be generated. Run the `trade progress`
feature, enter the number of rare tickets you want, go into game and press
the `Use All` button in cat storage and then press `Trade for Ticket` .
There appears to be nothing in your storage because there is an unobtainable blue
upgrade / special skill between `power` and `range` and the editor adds that to
your storage to allow you to use the `trade` thing, although any other blue
upgrade also works, as long as it is max level.

### Fixed

- More save parsing issues

## [1.6.2] - 2022-05-03

### Fixed

- Upgrade cats and upgrade blue upgrades crashing the editor

## [1.6.1] - 2022-05-03

### Fixed

- Gauntlets from crashing the editor

## [1.6.0] - 2022-05-03

### Added

- The ability to edit specific treasures for each stage

- The ability to edit groups of treasures (e.g energy drink, aqua crystal)

### Fixed

- More save parsing issues
- Event stages crashing when selecting `all` for the stage ids

## [1.5.0] - 2022-04-28

### Added

- When exporting to json, the current editor version will be included and so if
json data from a different editor version is being imported a warning
message will show.

- Option to edit specific stages in a main story chapter

- Option to remove enemy guide entries

### Fixed

- More save parsing issues

- Meow medals not writing properly

- The enemy ids in `unlock/remove enemy guide entries` not being the same as the
ones on the wiki

## [1.4.8] - 2022-04-23

### Fixed

- More save parsing issues
- Event stages, uncanny, gauntlets not unlocking the next subchapter
- Outbreaks crashing the editor after being edited

## [1.4.7] - 2022-04-22

### Changed

- It seems like the adb included in the editor doesn't work, and so I've removed
it, you now need to have adb in your Path environment variable. Tutorial
in the help videos's description

## [1.4.6] - 2022-04-22

### Changed

- When the editor detects a new version, it will display where to see the changelog

### Fixed

- A small issue relating to meow medals

- Some more parsing errors

## [1.4.5] - 2022-04-22

### Changed

- `adb.exe` is now included in the project, so you should be able to auto-pull
and push saves without adding it to your `PATH`

### Fixed

- Catfruit crashing

## [1.4.4] - 2022-04-21

### Fixed

- Some saves getting an error when parsing

## [1.4.2 & 1.4.3] - 2022-04-21

### Fixed

- It should correctly auto-install required packages

## [1.4.1] - 2022-04-21

### Fixed

- It should auto-install required packages

## [1.4.0] - 2022-04-21

### Added

- Ability to unlock enemy guide

- Ability to clear cat guide rewards

### Changed

- Made clear tutorial also beat Korea

## [1.3.0] - 2022-04-21

### Added

- Ability to add, upgrade cats, and true form cats in a certain rarity category.


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.



================================================
FILE: LOCALIZATION.md
================================================
# Localization

Small tutorial on how to localize the editor into a different language.

## Disclaimer

Please do not use machine or AI translated text, they will likely make mistakes, especially since
there is specific terminology unique to The Battle Cats and the save editor, and if you do not
know the language you will not be able to correct them. There are also many other ethical and legal
issues when using AI that I would like to avoid.

Thank you for understanding

## How To

0. If you want to submit a pull request later you should fork the editor (make sure to fork the codeberg repo: <https://codeberg.org/fieryhenry/BCSFE-Python>)

1. Install the editor from source by following [these instructions](https://codeberg.org/fieryhenry/BCSFE-Python#install-from-source)
  (make sure to change the git clone url to be your fork if you have one)

2. Inside the `src/bcsfe/files/locales/` folder you will find the pre-existing locales, copy the
  one named `en` and rename it to the code of the language you are translating to

3. Create a file called `metadata.json` inside the folder and edit it to contain the following info:

```json
{
  "authors": ["author-1", "author2", "cool-person3"],
  "name": "Name of language (english name of language)"
}
```

For example the one for Vietnamese looks like this:

```json
{
  "authors": ["HungJoesifer"],
  "name": "Tiếng Việt (Vietnamese)"
}
````

4. Edit each of the .properties file, translating each value, try to keep the colors the same as
the original text. Anything in `{..}` should stay exactly how it is. Anything in `{{..}}` references
another key and so can be changed if you want. For more details see [here](https://codeberg.org/fieryhenry/ExampleEditorLocale/).

5. Once you think you have finished, open the editor and edit the config value `Language` and
select your language from the list

6. Restart the editor and check that it works, you should also see the details you specified
in the `metadata.json` file in the opening text

7. Enable the config option to display missing locale keys then restart the editor

8. If everything is correct you shouldn't see any missing keys (extra keys are fine).

9. Once done, push your changes to your fork if you have one and feel free to submit a pull request
to the codeberg repo. Alternatively you can just zip your locale folder and send it to me or in
the #localization channel on discord. (or [matrix](https://matrix.to/#/@fieryhenry:matrix.battlecatsmodding.org))


================================================
FILE: MANIFEST.in
================================================
recursive-include src/bcsfe/files *
recursive-exclude src/bcsfe/files/game_data *
recursive-exclude src/bcsfe/files/map_names *

================================================
FILE: README.md
================================================
# Battle Cats Save File Editor

[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/fieryhenry)

[![LiberaPay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/fieryhenry)

BCSFE is a python command line save editor for The Battle Cats.

Join the [discord server](https://discord.gg/DvmMgvn5ZB) if you want to suggest
new features, report bugs or get help on how to use the editor (please read the
below tutorials first before asking for help).

## Thanks to

Lethal's editor for giving me inspiration to start the project and it helped me
work out how to patch the save data and edit cf/xp: <https://www.reddit.com/r/BattleCatsCheats/comments/djehhn/editoren/>

Beeven and csehydrogen's free and open source code, which helped me figure out how to
patch save data: [beeven/battlecats](https://github.com/beeven/battlecats), [csehydrogen/BattleCatsHacker](https://github.com/csehydrogen/BattleCatsHacker)

Anyone who has supported my on [Ko-Fi](https://ko-fi.com/fieryhenry) or [LiberaPay](https://liberapay.com/fieryhenry)

Everyone who's given me saves, which helped to test save loading/saving and to
test/develop new features

### Localization

- HungJoesifer for Vietnamese localization

- LinYuAn for Traditional Chinese localization

### Themes

- HungJoesifer for the `discord` inspired theme

## Installation

Note the following tutorials are for the device you wish to run the editor on, not the device
that you have the game installed on.

For example just because you have the game on an android device, does not mean you have to run
the editor on it. It is easier to run the editor on a PC / laptop rather than on a mobile device.

### Windows / MacOS

1. Install Python 3.9 or later if you don't already have it: <https://www.python.org/downloads/>

2. Open a terminal such as PowerShell or Command Prompt

3. Run the following command:

```powershell
py -m pip install bcsfe
```

4. If you get an error saying that `py` is not a recongnised command, then try:

```powershell
python -m pip install bcsfe
```

or

```powershell
python3 -m pip install bcsfe
```

5. If you get an error saying `No module named pip`, then run:

```powershell
py -m ensurepip --upgrade
```

Again change `py` for `python` or `python3` if needed. I won't mention this again, so just remember
the one which works at keep using that.

5. To run the editor, as long as Python is in your PATH, you should be able to run:

```powershell
bcsfe
```

6. If Python is not in your path you'll need to run:

```powershell
py -m bcsfe
```

If you are using Windows and you are still struggling, try watching this video [here](https://codeberg.org/fieryhenry/videos/media/branch/main/bcsfe_windows_help.webm).

7. To update the editor run:

```powershell
py -m pip install -U bcsfe
```

8. To uninstall the editor run:

```powershell
py -m pip uninstall bcsfe
```

### Linux

1. Install Python 3.9 or later using your system's package manager if you don't already have it

2. You might have to install pip seperately with a package called `python-pip` or something similar
or you can run the following command:

```sh
python3 -m ensurepip --upgrade
```

3. Depending on your distro you might not be able to install the editor directly using the system
pip and you might need to use pipx (python-pipx) or create a virtual environment manually.

4. Using pipx:

```sh
pipx install bcsfe
```

5. If `~/.local/bin/` is in your path you should be able to run the editor with the command:

```sh
bcsfe
```

6. You may also need to install `tk` with your system package manager to open the
file selection dialog. This package may be called `tk` or `python-tk` or `python3-tk`.

7. To update the editor if you are using pipx run:

```sh
pipx upgrade bcsfe
```

8. To uninstall the editor if you are using pipx run:

```sh
pipx uninstall bcsfe
```

If anyone wants to put the editor on the AUR or another package repo, feel free, I'll be happy to
help if needed.

### Android

You need to install a terminal emulator to be able to install and run Python packages.

[Termux](https://termux.dev/en/) is a good option and is what this tutorial will use.

1. Download Termux, you can either get it from [F-Droid](https://f-droid.org/), or the APK directly
from [GitHub](https://github.com/termux/termux-app?tab=readme-ov-file#github). DO NOT use the
Google Play Store version, as it does not fully work.

I recommend using F-Droid since it can update Termux for you (and it's just a better alternative
than using the Google Play Store).

On F-Droid Termux is called `Termux Terminal emulator with packages`

2. Once Termux is installed, open it and run the following commands:

```sh
termux-setup-storage
termux-change-repo
pkg update
pkg upgrade
pkg install python python-pip
```

When it asks for a mirror, it doesn't really matter which one you pick, the default single mirror
works fine.

3. Install the editor with the following command:

```sh
pip install bcsfe
```

Or if that doesn't work try:

```sh
python -m pip install bcsfe
```

4. Run the editor with the following command:

```sh
bcsfe
```

Or if that doesn't work try:

```sh
python -m bcsfe
```

Note that the editor might give you warnings about tkinter not being installed, you can just
ignore those as tkinter will not work on mobile. This just means that instead of a graphical file
selection dialog, you just have to type the file path manually.

For example to save your save file to your downloads directory, the path might look something like
`/storage/emulated/0/Download/SAVE_DATA` or `/sdcard/Download/SAVE_DATA`

5. To update the editor run:

```sh
pip install -U bcsfe
```

Or

```sh
python -m pip install -U bcsfe
```


5. To uninstall the editor run:

```sh
pip uninstall bcsfe
```

Or

```sh
python -m pip uninstall bcsfe
```

### iOS

I do not have an iOS device, so there is no tutorial. The video that was recommended is now outdated.
But for a general overview of what you need to do:

1. Download a-Shell from the App Store
2. Install the editor with:

```sh
pip install bcsfe
```

3. Run the editor with:

```sh
bcsfe
```

Or if that doesn't work try:

```sh
python -m bcsfe
```

Or 

```sh
python3 -m bcsfe
```

4. To update the editor run:

```sh
pip install -U bcsfe
```

5. To uninstall the editor run:

```sh
pip uninstall bcsfe
```
## Terms of Use

I don't like that I have to have Terms of use but these terms are designed to prevent scams and the
exploitation of users.

By using the editor you agree to the following:

If you are using the editor to run a paid service that profits off of the editor
(e.g a service to provide people with hacked accounts, or a paid discord bot to edit people's accounts,
etc) you must make it very clear that you are using this save editor.

This should be done by linking this Codeberg page, and explicitly stating that the tool you are
using is available for free and that they don't need to use your service to hack their account.

This information needs to be visible and something the customer agrees to **before** any payment is made.

This also includes paid services which claim to teach people "How To Hack The Battle Cats". In those
cases, this still applies, so you still need to state and have the customer acknowledge the things
I said above.

Free services / derivative works (such as a third party discord bot or editor gui) are fine to use
the editor under the hood as long as you abide by the [License](#license). Basically if you are
distributing a program which uses the editor, you need to license your own program under the GPL
or a compatible license (basically make it open source / free software too).

Also if you **are** profiting from the editor, it would be greatly appreciated if you could
give back something and support me.

## Usage

Once you have installed and ran the editor, you can now begin to edit your save file!

1. In `The Battle Cats` enter the `Change Account / Device` menu in the `Settings` on the main menu.

2. Then enter the `Change Device` -> `Retrieve Data from Old Device` menu.

3. Then click / tap `Save Data to Server`, this should give you a transfer code and a confirmation
code.

4. In the editor use the option called `Download save file using transfer and
confirmation code` by entering the number `1`

5. Enter your transfer code

6. Enter your confirmation code

7. Select the country code that you are using, `en`=english,
`kr`=korean, `jp`=japanese, `tw`=taiwanese.

Note that `en` also includes the `it`, `es`, `fr`, `th`, and `de` translations.

8. Edit what you want. Note that in most cases, if you want to exit the current
   input you can enter `q` and press enter to go back to the previous menu

9. In the editor, go into the `Save Management` category and select `Save changes and upload to
game servers (get transfer and confirmation codes)`. It may take some time, it
may also fail, if it does then try again.

10. This should give you a new transfer code and a new confirmation code.

11. Back in-game, tap the `Close Game` button, then tap `Cancel Data Transfer` (and also possibly
`Start Game From Beginning`)

12. Go back into the `Change Account / Device` menu and then go into the `Resume Data Transfer` ->
`Transfer Data to New Device` menu

13. Enter the new codes, and tap `Resume Transfer`

14. Then done! You should see your edits in-game.

15. Every time that you want to make an edit to your save, you will have to re-upload it to the
game servers in the editor and re-download it in-game, the saves aren't automatically linked together.

Apparently doing the Google Account / Apple Account link limits the number of data transfers you can
do within a certain time. So to be safe, I would avoid linking your account.

### Using a rooted device via adb

1. Add adb to your PATH environment variable, or edit the config to set ADB path editor config option
  to the full path of the adb executable. You can download adb from
  [platform-tools](https://developer.android.com/studio/releases/platform-tools)

1. Open the editor and select the option named `Pull save file from device
using adb` and enter your game version, or select the option named
`Select save file from file` and select a copy of your save data

1. Edit what you want

1. Go into save management and select an option to push save data to the game

1. Enter the game and you should see changes

### Using a rooted device directly

1. You need to be running the editor on the device itself, so you'll need to
follow the [Android tutorial](#android) to install the editor

1. You may have to run the editor with `sudo python -m bcsfe` or something, so you might have to
setup the termux root repo and run `pkg install sudo`

1. In the editor select the option named `Pull save file from root storage`

1. Edit what you want

1. Go into save management and select an option to push save data to the game

1. Enter the game and you should see changes


### How to unban your account

1. Select the option in `Account` to `Unban account` or
just upload the save data to the game servers again

1. It may take some time but after, you should be able to choose one of the
options in save management to push the save data to the game.

#### How to prevent a ban in the future

- Instead of editing in platinum tickets use the `Platinum Shards` feature

- Instead of editing in rare tickets use the `Normal Ticket Max Trade Progress
(allows for unbannable rare tickets)` feature

- Instead of hacking in cat food, just edit everything in that you can buy with
cat food, e.g battle items, catamins, xp, energy refills (leaderships), etc.
If you really want catfood then you can clear and unclear catnip missions with
the feature `Catnip Challenges / Missions` then entering 1 when asked.
You'll need to collect the catfood in-game after each clear though

- Instead of hacking in tickets, just hack in the cats/upgrades you want directly

### Install from source

If you want the latest features then you can install the editor from the git repo.

1. Download git:
    - Windows: [Git](https://git-scm.com/downloads)
    - Linux: (use package manager, e.g `sudo apt-get install git` or `sudo pacman -S git`)
    - Android: Termux: `pkg install git`
    - iOS: a-Shell should already include it

2. Run the following commands: (You may have to replace `py` with `python` or `python3`)

```sh
git clone https://codeberg.org/fieryhenry/BCSFE-Python.git
cd BCSFE-Python
py -m pip install -e .
py -m bcsfe
```

Then if you want the latest changes you only need to run `git pull` in the downloaded
`BCSFE-Python` folder. (use `cd` to change the folder)

Alternatively you can use pip directly, although it won't auto-update with the latest
git commits.

```sh
py -m pip install -U git+https://codeberg.org/fieryhenry/BCSFE-Python.git
py -m bcsfe
```

Again, you might need change `py` for `python` or `python3`

If you want to use the editor again all you need to do is run the `py -m bcsfe` command

## Documentation

- [Custom Editor Locales](https://codeberg.org/fieryhenry/ExampleEditorLocale)
- [Custom Editor Themes](https://codeberg.org/fieryhenry/ExampleEditorTheme)

I only have documentation for the locales and themes atm, but I will probably
add more documentation in the future.

## Contributing

If you want to contribute to the BCSFE, I recommend joining the [Discord Server](https://discord.gg/DvmMgvn5ZB) and starting a
discussion in #dev-chat, or create an issue in this repo, or a draft pull request.

If you need help with reverse engineering the save file, I have a basic starting guide here:
<https://codeberg.org/fieryhenry/bc_ree>.

If you want to localize the editor see [here](./LOCALIZATION.md).

## License

BCSFE is licensed under the GNU GPLv3 which can be read [here](https://www.gnu.org/licenses/gpl-3.0.en.html).


================================================
FILE: pyproject.toml
================================================
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "bcsfe"
authors = [{ name = "fieryhenry" }]
description = "A save file editor for The Battle Cats"
license = "GPL-3.0-or-later"
readme = "README.md"
requires-python = ">=3.9"
classifiers = [
  "Development Status :: 4 - Beta",
  "Intended Audience :: End Users/Desktop",
  "Topic :: Utilities",
  "Programming Language :: Python :: 3",
  "Programming Language :: Python :: 3.9",
  "Operating System :: OS Independent",
]
dependencies = [
  "aenum",
  "colored==1.4.4",
  "pyjwt",
  "requests",
  "pyyaml",
  "beautifulsoup4",
  "argparse",
]
dynamic = ["version"]
keywords = ["battle cats", "save editor", "hacking"]

[project.urls]
Homepage = "https://codeberg.org/fieryhenry/BCSFE-Python"
Repository = "https://codeberg.org/fieryhenry/BCSFE-Python"
Issues = "https://codeberg.org/fieryhenry/BCSFE-Python/issues"
Changelog = "https://codeberg.org/fieryhenry/BCSFE-Python/raw/branch/main/CHANGELOG.md"

[tool.setuptools.dynamic]
version = { attr = "bcsfe.__version__" }

[tool.setuptools]
package-dir = { "" = "src" }

[project.scripts]
bcsfe = "bcsfe:run"


================================================
FILE: requirements.txt
================================================
aenum==3.1.16
colored==1.4.4
PyJWT==2.12.1
PyYAML==6.0.2
Requests==2.33.1
beautifulsoup4==4.13.4
argparse==1.4.0


================================================
FILE: setup.py
================================================
from setuptools import setup

setup()


================================================
FILE: src/bcsfe/__init__.py
================================================
__version__ = "3.3.0"

from bcsfe import core, cli


__all__ = ["core", "cli"]


def run():
    from bcsfe import __main__

    __main__.main()


================================================
FILE: src/bcsfe/__main__.py
================================================
from __future__ import annotations
import traceback

from bcsfe import cli

from bcsfe import core

import bcsfe
import argparse


def main():
    parser = argparse.ArgumentParser("bcsfe")

    parser.add_argument(
        "--version", "-v", action="store_true", help="display the version and exit"
    )
    parser.add_argument(
        "--input-path", "-i", type=str, help="input path to save file to edit"
    )
    parser.add_argument(
        "--game-data-dir", "-g", type=str, help="path to store the game data to"
    )
    parser.add_argument(
        "--transfer-backup-path",
        type=str,
        help="path to save the backup SAVE_DATA after transfering to",
    )
    parser.add_argument(
        "--config-path",
        "-c",
        type=str,
        default=None,
        help=f"path to the config file. If unspecified defaults to {core.Config.get_config_path()}",
    )
    parser.add_argument(
        "--log-path",
        "-l",
        type=str,
        default=None,
        help=f"path to the log file. If unspecified defaults to {core.Logger.get_log_path()}",
    )

    args = parser.parse_args()
    if args.version:
        print(bcsfe.__version__)
        exit()

    if args.config_path is not None:
        core.set_config_path(core.Path(args.config_path))

    if args.log_path is not None:
        core.set_log_path(core.Path(args.log_path))

    if args.transfer_backup_path is not None:
        core.set_transfer_backup_path(core.Path(args.transfer_backup_path))

    if args.game_data_dir is not None:
        core.set_game_data_path(core.Path(args.game_data_dir))

    core.core_data.init_data()

    try:
        cli.main.Main().main(args.input_path)
    except KeyboardInterrupt:
        cli.main.Main.leave()
    except Exception as e:
        tb = traceback.format_exc()
        cli.color.ColoredText.localize(
            "error", error=e, version=bcsfe.__version__, traceback=tb
        )
        try:
            cli.main.Main.exit_editor()
        except Exception:
            pass
        except KeyboardInterrupt:
            pass


main()


================================================
FILE: src/bcsfe/cli/__init__.py
================================================
from bcsfe.cli import (
    color,
    dialog_creator,
    main,
    file_dialog,
    feature_handler,
    save_management,
    server_cli,
    edits,
    recent_saves,
)

__all__ = [
    "color",
    "dialog_creator",
    "main",
    "file_dialog",
    "feature_handler",
    "save_management",
    "server_cli",
    "edits",
    "recent_saves",
]


================================================
FILE: src/bcsfe/cli/color.py
================================================
from __future__ import annotations
from typing import Any
from aenum import NamedConstant  # type: ignore
import colored  # type: ignore
from bcsfe import core


class ColorHex(NamedConstant):
    GREEN = "#008000"
    G = GREEN
    RED = "#FF0000"
    R = RED
    DARK_YELLOW = "#D7C32A"
    DY = DARK_YELLOW
    BLACK = "#000000"
    BL = BLACK
    WHITE = "#FFFFFF"
    W = WHITE
    CYAN = "#00FFFF"
    C = CYAN
    DARK_GREY = "#A9A9A9"
    DG = DARK_GREY
    BLUE = "#0000FF"
    B = BLUE
    YELLOW = "#FFFF00"
    Y = YELLOW
    MAGENTA = "#FF00FF"
    M = MAGENTA
    DARK_BLUE = "#00008B"
    DB = DARK_BLUE
    DARK_CYAN = "#008B8B"
    DC = DARK_CYAN
    DARK_MAGENTA = "#8B008B"
    DM = DARK_MAGENTA
    DARK_RED = "#8B0000"
    DR = DARK_RED
    DARK_GREEN = "#006400"
    DGN = DARK_GREEN
    LIGHT_GREY = "#D3D3D3"
    LG = LIGHT_GREY
    ORANGE = "#FFA500"
    O = ORANGE

    @staticmethod
    def from_name(name: str) -> str:
        if name == "":
            return ""
        return getattr(ColorHex, name.upper())


class ColorHelper:
    def __init__(self):
        self.theme_handler = core.core_data.theme_manager

    def get_color(self, color_name: str) -> str:
        try:
            first_char = color_name[0]
        except IndexError:
            return ""
        if first_char == "#":
            return color_name
        if first_char == "@":
            try:
                second_char = color_name[1]
            except IndexError:
                return ""
            try:
                third_char = color_name[2]
            except IndexError:
                third_char = ""
            if second_char == "p":
                return self.theme_handler.get_primary_color()
            if second_char == "s" and third_char != "u":
                return self.theme_handler.get_secondary_color()
            if second_char == "t":
                return self.theme_handler.get_tertiary_color()
            if second_char == "q":
                return self.theme_handler.get_quaternary_color()
            if second_char == "e":
                return self.theme_handler.get_error_color()
            if second_char == "w":
                return self.theme_handler.get_warning_color()
            if second_char == "s" and third_char == "u":
                return self.theme_handler.get_success_color()
            return self.theme_handler.get_theme_color(color_name[1:])
        try:
            return ColorHex.from_name(color_name)
        except AttributeError:
            return ""


class ColoredText:
    def __init__(self, string: str, end: str = "\n") -> None:
        string = string.replace("\\n", "\n")
        self.string = string
        self.end = end
        self.color_helper = ColorHelper()
        self.display(string)

    def display(self, string: str) -> None:
        text_data = self.parse(string)
        for i, (text, color) in enumerate(text_data):
            if i == len(text_data) - 1:
                text += self.end
            if color == "":
                print(text, end="")
            else:
                try:
                    fg = colored.fg(color)  # type: ignore
                except Exception:
                    print(text, end="")
                    continue
                print(colored.stylize(text, fg), end="")  # type: ignore

    @staticmethod
    def localize(string: str, escape: bool = True, **kwargs: Any) -> ColoredText:
        return ColoredText(
            core.core_data.local_manager.get_key(string, escape=escape, **kwargs)
        )

    def parse(self, txt: str) -> list[tuple[str, str]]:
        txt = "<@p>" + txt + "</>"
        output: list[tuple[str, str]] = []
        i = 0
        tags: list[str] = []
        inside_tag = False
        in_closing_tag = False
        tag_text = ""
        text = ""
        special_chars = core.LocalManager.get_special_chars()
        while i < len(txt):
            char = txt[i]
            if char == "\\" and i + 1 < len(txt) and txt[i + 1] in special_chars:
                i += 1
                char = txt[i]
                text += char
                i += 1
                continue
            if tags:
                tag = tags[-1]
            else:
                tag = ""
            if char == ">" and inside_tag:
                inside_tag = False
                if not in_closing_tag:
                    tags.append(tag_text)
                if in_closing_tag:
                    in_closing_tag = False
                tag_text = ""
            if char == "<" and not inside_tag:
                inside_tag = True
                if text:
                    color = self.color_helper.get_color(tag)
                    output.append((text, color))
                    text = ""
                    tag_text = ""
            if char == "/" and inside_tag:
                in_closing_tag = True
                if tags:
                    tags.pop()
            if not inside_tag and char != ">" and char != "<":
                text += char
            if inside_tag and char != "<" and char != ">":
                tag_text += char
            i += 1
        return output


class ColoredInput:
    def __init__(self, end: str = "") -> None:
        self.end = end

    def get(self, display_string: str) -> str:
        ColoredText(display_string, end=self.end)
        return input()

    def localize(self, string: str, escape: bool = True, **kwargs: Any) -> str:
        text = core.core_data.local_manager.get_key(string, escape=escape, **kwargs)
        return self.get(text)


================================================
FILE: src/bcsfe/cli/dialog_creator.py
================================================
from __future__ import annotations
from typing import Any
from bcsfe import core
from bcsfe.cli import color


class RangeInput:
    def __init__(self, max: int | None = None, min: int = 0):
        self.max = max
        self.min = min

    def clamp_value(self, value: int) -> int:
        if self.max is None:
            return max(value, self.min)
        return max(min(value, self.max), self.min)

    def get_input_locale(
        self,
        dialog: str,
        perameters: dict[str, int | str],
        escape: bool = True,
    ) -> list[int] | None:
        user_input = color.ColoredInput(end="").localize(
            dialog, escape=escape, **perameters
        )
        return self.parse(user_input)

    def parse(self, user_input: str) -> list[int] | None:
        if user_input == "":
            return []
        if user_input == core.core_data.local_manager.get_key("quit_key"):
            return None
        parts = user_input.split(" ")
        ids: list[int] = []
        all_text = core.core_data.local_manager.get_key("all")
        for part in parts:
            if "-" in part and len(part.split("-")) == 2:
                lower, upper = part.split("-")
                try:
                    lower = int(lower)
                    upper = int(upper)
                except ValueError:
                    continue
                if lower > upper:
                    lower, upper = upper, lower
                if self.max is not None:
                    lower = max(lower, self.min)
                    upper = min(upper, self.max)
                else:
                    lower = max(lower, self.min)
                ids.extend(range(lower, upper + 1))
            elif part.lower() == all_text.lower() and self.max is not None:
                ids.extend(range(self.min, self.max + 1))
            else:
                try:
                    part = int(part)
                except ValueError:
                    continue
                if self.max is not None:
                    part = max(part, self.min)
                    part = min(part, self.max)
                else:
                    part = max(part, self.min)
                ids.append(part)
        return ids


class IntInput:
    def __init__(
        self,
        max: int | None = None,
        min: int = 0,
        default: int | None = None,
        signed: bool = True,
        bit_count: int = 32,
        ensure_max: bool = False,
    ):
        self.signed = signed
        self.bit_count = bit_count
        self.max = self.get_max_value(max, signed, bit_count, ensure_max)
        self.min = min
        self.default = default

    @staticmethod
    def get_max_value(
        max: int | None,
        signed: bool = True,
        bit_count: int = 32,
        ensure_max: bool = False,
    ) -> int:
        disable_maxes = (
            core.core_data.config.get_bool(core.ConfigKey.DISABLE_MAXES)
            and not ensure_max
        )
        if signed:
            bit_count -= 1
        max_int = (2**bit_count) - 1
        if disable_maxes or max is None:
            return max_int
        return min(max, max_int)

    def clamp_value(self, value: int) -> int:
        return max(min(value, self.max), self.min)

    def get_input(
        self,
        localization_key: str,
        perameters: dict[str, int | str],
        escape: bool = True,
    ) -> tuple[int | None, str]:
        user_input = color.ColoredInput(end="").localize(
            localization_key, escape=escape, **perameters
        )
        if user_input == "" and self.default is not None:
            return self.default, user_input
        try:
            user_input_i = int(user_input)
        except ValueError:
            return None, user_input

        return self.clamp_value(user_input_i), user_input

    def get_input_locale_while(
        self, dialog: str, perameters: dict[str, int | str], escape: bool = True
    ) -> int | None:
        while True:
            int_val, user_input = self.get_input(dialog, perameters, escape=escape)
            if int_val is not None:
                return int_val
            if user_input == core.core_data.local_manager.get_key("quit_key"):
                return None

    def get_input_locale(
        self, localization_key: str | None, perameters: dict[str, int | str]
    ) -> tuple[int | None, str]:
        if localization_key is None:
            if self.default is not None:
                perameters = {
                    "min": self.min,
                    "max": self.max,
                    "default": self.default,
                }
                localization_key = "input_int_default"
            else:
                perameters = {"min": self.min, "max": self.max}
                localization_key = "input_int"
        return self.get_input(localization_key, perameters)

    def get_basic_input_locale(self, localization_key: str, perameters: dict[str, Any]):
        try:
            user_input = int(
                color.ColoredInput(end="").localize(localization_key, **perameters)
            )
        except ValueError:
            return None
        return user_input


class ListOutput:
    def __init__(
        self,
        strings: list[str],
        ints: list[int] | list[str],
        dialog: str | None = None,
        perameters: dict[str, Any] | None = None,
        start_index: int = 1,
        localize_elements: bool = True,
    ):
        self.strings = strings
        self.ints = ints
        self.dialog = dialog
        if perameters is None:
            perameters = {}
        self.perameters = perameters
        self.start_index = start_index
        self.localize_elements = localize_elements

    def get_output(self, dialog: str | None, strings: list[str]) -> str:
        end_string = ""
        if dialog is not None:
            end_string = core.core_data.local_manager.get_key(dialog, **self.perameters)
        end_string += "\n"
        for i, string in enumerate(strings):
            try:
                int_string = str(self.ints[i])
            except IndexError:
                int_string = ""

            string = string.replace("{int}", int_string)
            end_string += f" <@s>{i + self.start_index}.</> <@t>{string}</>\n"
        end_string = end_string.strip("\n")
        return end_string

    def display(self, dialog: str | None, strings: list[str]) -> None:
        output = self.get_output(dialog, strings)
        color.ColoredText(output)

    def display_locale(self, remove_alias: bool = False) -> None:
        dialog = ""
        if self.dialog is not None:
            dialog = core.core_data.local_manager.get_key(self.dialog)
        new_strings: list[str] = []
        for string in self.strings:
            if self.localize_elements:
                string_ = core.core_data.local_manager.get_key(string)
            else:
                string_ = string
            if remove_alias:
                string_ = core.core_data.local_manager.get_all_aliases(string_)[0]
            new_strings.append(string_)

        self.display(dialog, new_strings)

    def display_non_locale(self) -> None:
        self.display(self.dialog, self.strings)


class ChoiceInput:
    def __init__(
        self,
        items: list[str],
        strings: list[str],
        ints: list[int] | list[str],
        perameters: dict[str, int | str],
        dialog: str,
        single_choice: bool = False,
        remove_alias: bool = False,
        display_all_at_once: bool = True,
        start_index: int = 1,
        localize_options: bool = True,
    ):
        self.items = items
        self.strings = strings
        self.ints = ints
        self.perameters = perameters
        self.dialog = dialog
        self.is_single_choice = single_choice
        self.remove_alias = remove_alias
        self.display_all_at_once = display_all_at_once
        self.start_index = start_index
        self.localize_options = localize_options

    @staticmethod
    def from_reduced(
        items: list[str],
        ints: list[int] | list[str] | None = None,
        perameters: dict[str, int | str] | None = None,
        dialog: str | None = None,
        single_choice: bool = False,
        remove_alias: bool = False,
        display_all_at_once: bool = True,
        start_index: int = 1,
        localize_options: bool = True,
    ) -> ChoiceInput:
        if perameters is None:
            perameters = {}
        if ints is None:
            ints = []
        if dialog is None:
            dialog = ""
        return ChoiceInput(
            items.copy(),
            items.copy(),
            ints.copy(),
            perameters.copy(),
            dialog,
            single_choice,
            remove_alias,
            display_all_at_once,
            start_index,
            localize_options,
        )

    def get_input(self) -> tuple[int | None, str]:
        if len(self.strings) == 0:
            return None, ""
        if len(self.strings) == 1:
            return self.get_min_value(), ""
        ListOutput(
            self.strings,
            self.ints,
            start_index=self.start_index,
            localize_elements=self.localize_options,
        ).display_locale(self.remove_alias)
        return IntInput(
            self.get_max_value(), self.get_min_value(), ensure_max=True
        ).get_input_locale(self.dialog, self.perameters)

    def get_input_while(self) -> int | None:
        if len(self.strings) == 0:
            return None
        while True:
            int_val, user_input = self.get_input()
            if int_val is not None:
                return int_val
            if user_input == core.core_data.local_manager.get_key("quit_key"):
                return None
            for i, string in enumerate(self.strings):
                if self.localize_options:
                    string = core.core_data.local_manager.get_key(string)
                if string.lower().strip() == user_input.lower().strip():
                    return i + self.start_index

    def get_max_value(self) -> int:
        return len(self.strings) + self.start_index - 1

    def get_min_value(self) -> int:
        return self.start_index

    def get_input_locale(self, localized: bool = True) -> tuple[list[int] | None, bool]:
        if len(self.strings) == 0:
            return [], False
        if len(self.strings) == 1:
            return [self.get_min_value()], False
        if not self.is_single_choice and self.display_all_at_once:
            if localized:
                self.strings.append("all_at_once")
            else:
                self.strings.append(core.core_data.local_manager.get_key("all_at_once"))
        if localized:
            ListOutput(
                self.strings,
                self.ints,
                start_index=self.start_index,
                localize_elements=self.localize_options,
            ).display_locale()
        else:
            ListOutput(
                self.strings,
                self.ints,
                start_index=self.start_index,
                localize_elements=self.localize_options,
            ).display_non_locale()
        key = "input_many"
        if self.is_single_choice:
            key = "input_single"
        dialog = core.core_data.local_manager.get_key(key).format(
            min=self.get_min_value(), max=self.get_max_value()
        )
        usr_input = color.ColoredInput().get(dialog).strip().split(" ")
        int_vals: list[int] = []
        for inp in usr_input:
            try:
                value = int(inp)
                if value > self.get_max_value() or value < self.get_min_value():
                    raise ValueError
                int_vals.append(value)
            except ValueError:
                if inp == core.core_data.local_manager.get_key("quit_key"):
                    return None, False

                cont = False
                for i, string in enumerate(self.strings):
                    if self.localize_options:
                        string = core.core_data.local_manager.get_key(string)
                    if string.lower().strip() == inp.lower().strip():
                        int_vals.append(i + self.start_index)
                        cont = True
                        break

                if cont:
                    continue

                color.ColoredText.localize(
                    "invalid_input_int",
                    min=self.get_min_value(),
                    max=self.get_max_value(),
                )
        if (
            self.get_max_value() in int_vals
            and not self.is_single_choice
            and self.display_all_at_once
        ):
            return list(range(self.get_min_value(), self.get_max_value())), True

        if self.is_single_choice and len(int_vals) > 1:
            int_vals = [int_vals[0]]

        return int_vals, False

    def get_input_locale_while(self) -> list[int] | None:
        if len(self.strings) == 0:
            return []
        if len(self.strings) == 1:
            return [self.get_min_value()]
        while True:
            int_vals, all_at_once = self.get_input_locale()
            if int_vals is None:
                return None
            if all_at_once:
                return int_vals
            if len(int_vals) == 0:
                continue
            if len(int_vals) == 1 and int_vals[0] == 0:
                return []
            return int_vals

    def multiple_choice(
        self, localized_options: bool = True
    ) -> tuple[list[int] | None, bool]:
        color.ColoredText.localize(self.dialog, True, **self.perameters)
        user_input, all_at_once = self.get_input_locale(localized_options)
        if user_input is None:
            return None, all_at_once
        return [i - self.start_index for i in user_input], all_at_once

    def single_choice(self) -> int | None:
        return self.get_input_while()

    def get(self) -> tuple[int | None | list[int], bool]:
        if self.is_single_choice:
            return self.single_choice(), False
        return self.multiple_choice()


class MultiEditor:
    def __init__(
        self,
        group_name: str,
        items: list[str],
        strings: list[str],
        ints: list[int] | None,
        max_values: list[int] | int | None,
        perameters: dict[str, int | str] | None,
        dialog: str,
        single_choice: bool = False,
        signed: bool = True,
        group_name_localized: bool = False,
        cumulative_max: bool = False,
        bit_count: int = 32,
    ):
        self.items = items
        self.strings = strings
        self.ints = ints
        self.bit_count = bit_count
        if self.ints is not None:
            total_ints = len(self.ints)
        else:
            total_ints = len(self.strings)
        if max_values is None:
            max_values_ = [None] * total_ints
        elif isinstance(max_values, int):
            max_values_ = [max_values] * total_ints
        else:
            max_values_ = max_values
        self.max_values = max_values_
        if perameters is None:
            perameters = {}
        self.perameters = perameters
        if group_name_localized:
            self.perameters["group_name"] = core.core_data.local_manager.get_key(
                group_name
            )
        else:
            self.perameters["group_name"] = group_name
        self.dialog = dialog
        self.is_single_choice = single_choice
        self.signed = signed
        self.cumulative_max = cumulative_max

    @staticmethod
    def from_reduced(
        group_name: str,
        items: list[str],
        ints: list[int] | None,
        max_values: list[int] | int | None,
        group_name_localized: bool = False,
        dialog: str = "input",
        cumulative_max: bool = False,
        items_localized: bool = False,
    ):
        if items_localized:
            for i, item in enumerate(items):
                items[i] = core.core_data.local_manager.get_key(item)
        text: list[str] = []
        for item_name in items:
            if ints is not None:
                text.append(f"{item_name} <@q>: {{int}}</>")
            else:
                text.append(f"{item_name}")
        return MultiEditor(
            group_name,
            items,
            text,
            ints,
            max_values,
            None,
            dialog,
            group_name_localized=group_name_localized,
            cumulative_max=cumulative_max,
        )

    def edit(self) -> list[int]:
        choices, all_at_once = ChoiceInput(
            self.items,
            self.strings,
            self.ints or [],  # type: ignore
            self.perameters,
            "select_edit",
        ).get()
        if choices is None:
            return self.ints or []
        if isinstance(choices, int):
            choices = [choices]
        if all_at_once:
            return self.edit_all(choices)
        return self.edit_one(choices)

    def edit_all(self, choices: list[int]) -> list[int]:
        max_max_value = 0
        for choice in choices:
            if choice >= len(self.max_values):
                max_value = None
            else:
                max_value = self.max_values[choice]
            if max_value is None:
                max_value = IntInput.get_max_value(
                    max_value, self.signed, self.bit_count
                )
            max_max_value = max(max_max_value, max_value)
        if self.cumulative_max:
            max_max_value = max_max_value // len(choices)
        usr_input = IntInput(max_max_value, default=None).get_input_locale_while(
            self.dialog + "_all",
            {
                "name": self.perameters["group_name"],
                "max": max_max_value,
            },
        )
        if usr_input is None:
            return self.ints or []
        ints = self.ints or [0] * len(self.strings)

        for choice in choices:
            if choice >= len(self.max_values):
                max_value = None
            else:
                max_value = self.max_values[choice]
            max_value = IntInput.get_max_value(max_value, self.signed, self.bit_count)
            value = min(usr_input, max_value)
            ints[choice] = value
            if self.ints is not None:
                color.ColoredText.localize(
                    "value_changed",
                    name=self.items[choice],
                    value=value,
                )

        return ints

    def edit_one(self, choices: list[int]) -> list[int]:
        ints = self.ints or [0] * len(self.strings)

        for choice in choices:
            if choice >= len(self.max_values):
                max_value = None
            else:
                max_value = self.max_values[choice]
            if max_value is None:
                max_value = IntInput.get_max_value(
                    max_value, self.signed, self.bit_count
                )

            if self.cumulative_max:
                max_value -= sum(ints) - ints[choice]
                max_value = max(max_value, 0)

            item = self.items[choice]
            usr_input = IntInput(
                max_value, default=ints[choice]
            ).get_input_locale_while(
                self.dialog,
                {"name": item, "value": ints[choice], "max": max_value},
                escape=False,
            )
            if usr_input is None:
                continue
            ints[choice] = usr_input
            color.ColoredText.localize(
                "value_changed", name=item, value=ints[choice], escape=False
            )
        return ints


class SingleEditor:
    def __init__(
        self,
        item: str,
        value: int,
        max_value: int | None = None,
        min_value: int = 0,
        signed: bool = True,
        localized_item: bool = False,
        remove_alias: bool = False,
        bit_count: int = 32,
    ):
        if localized_item:
            item = core.core_data.local_manager.get_key(item)
        if remove_alias:
            item = core.core_data.local_manager.get_all_aliases(item)[0]
        self.item = item
        self.value = value
        self.max_value = max_value
        self.min_value = min_value
        self.signed = signed
        self.bit_count = bit_count

    def edit(self, escape_text: bool = True) -> int:
        max_value = IntInput.get_max_value(self.max_value, self.signed, self.bit_count)
        if self.max_value is None:
            dialog = "input_non_max"
        elif self.min_value != 0:
            dialog = "input_min"
        else:
            dialog = "input"
        usr_input = IntInput(
            max_value,
            self.min_value,
            default=self.value,
            signed=self.signed,
            bit_count=self.bit_count,
        ).get_input_locale_while(
            dialog,
            {
                "name": self.item,
                "value": self.value,
                "max": max_value,
                "min": self.min_value,
            },
            escape=escape_text,
        )
        if usr_input is None:
            return self.value
        print()
        color.ColoredText.localize(
            "value_changed", name=self.item, value=usr_input, escape=escape_text
        )
        return usr_input


class StringInput:
    def __init__(self, default: str = ""):
        self.default = default

    def get_input_locale_while(
        self, key: str, perameters: dict[str, Any]
    ) -> str | None:
        while True:
            usr_input = self.get_input_locale(key, perameters)
            if usr_input is None:
                return None
            if usr_input == "":
                return self.default
            if usr_input == " ":
                continue
            return usr_input

    def get_input_locale(
        self,
        key: str,
        perameters: dict[str, Any] | None = None,
        escape: bool = True,
    ) -> str | None:
        if perameters is None:
            perameters = {}
        usr_input = color.ColoredInput().localize(key, escape, **perameters)
        quit_key = core.core_data.local_manager.get_key("quit_key")
        if usr_input == "" or usr_input == quit_key:
            return None
        if usr_input == f"\\{quit_key}":
            return quit_key
        return usr_input


class StringEditor:
    def __init__(self, item: str, value: str, item_localized: bool = False):
        if item_localized:
            item = core.core_data.local_manager.get_key(item)
        self.item = item
        self.value = value

    def edit(self) -> str:
        usr_input = StringInput(default=self.value).get_input_locale_while(
            "input_non_max",
            {"name": self.item, "value": self.value},
        )
        if usr_input is None:
            return self.value
        color.ColoredText.localize(
            "value_changed",
            name=self.item,
            value=usr_input,
        )
        return usr_input


class YesNoInput:
    def __init__(self, default: bool = False):
        self.default = default

    def get_input_once(
        self, key: str, perameters: dict[str, Any] | None = None
    ) -> bool | None:
        if perameters is None:
            perameters = {}
        usr_input = color.ColoredInput().localize(key, **perameters)
        if usr_input == "":
            return self.default

        if usr_input == core.core_data.local_manager.get_key("quit_key"):
            return None
        return (
            usr_input == core.core_data.local_manager.get_key("yes_key")
            or usr_input.lower().strip()
            == core.core_data.local_manager.get_key("yes").lower().strip()
        )


class DialogBuilder:
    def __init__(self, dialog_structure: dict[Any, Any]):
        self.dialog_structure = dialog_structure


================================================
FILE: src/bcsfe/cli/edits/__init__.py
================================================
from bcsfe.cli.edits import (
    basic_items,
    cat_editor,
    clear_tutorial,
    rare_ticket_trade,
    fixes,
    enemy_editor,
    aku_realm,
    map,
    event_tickets,
    max_all,
    storage,
)

__all__ = [
    "basic_items",
    "cat_editor",
    "clear_tutorial",
    "rare_ticket_trade",
    "fixes",
    "enemy_editor",
    "aku_realm",
    "map",
    "event_tickets",
    "max_all",
    "storage",
]


================================================
FILE: src/bcsfe/cli/edits/aku_realm.py
================================================
from __future__ import annotations
from bcsfe import core
from bcsfe.cli import color


def unlock_aku_realm(save_file: core.SaveFile):
    stage_ids = [255, 256, 257, 258, 265, 266, 268]
    for stage_id in stage_ids:
        save_file.event_stages.clear_map(1, stage_id, 0, False)

    color.ColoredText.localize("aku_realm_unlocked")


================================================
FILE: src/bcsfe/cli/edits/basic_items.py
================================================
from __future__ import annotations
import random
from bcsfe import core
from bcsfe.cli import dialog_creator, color, edits
from bcsfe.core.game.catbase.gatya_item import GatyaItemCategory


class BasicItems:
    @staticmethod
    def get_name(name: str | None, key: str) -> str:
        if name is None:
            return core.core_data.local_manager.get_key(key)
        return name.strip()

    @staticmethod
    def reset_golden_cat_cpus(save_file: core.SaveFile):
        save_file.golden_cpu_count = 0

        color.ColoredText.localize("reset_golden_cat_cpus_success")

    @staticmethod
    def edit_catfood(save_file: core.SaveFile):
        should_exit = not dialog_creator.YesNoInput().get_input_once("catfood_warning")
        if should_exit:
            return

        name = core.core_data.get_gatya_item_names(save_file).get_name(22)
        original_amount = save_file.catfood
        save_file.catfood = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "catfood"),
            save_file.catfood,
            core.core_data.max_value_manager.get("catfood"),
        ).edit()
        change = save_file.catfood - original_amount
        core.BackupMetaData(save_file).add_managed_item(
            core.ManagedItem.from_change(change, core.ManagedItemType.CATFOOD)
        )

    @staticmethod
    def edit_xp(save_file: core.SaveFile):
        name = core.core_data.get_gatya_item_names(save_file).get_name(6)
        save_file.xp = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "xp"),
            save_file.xp,
            core.core_data.max_value_manager.get("xp"),
        ).edit()

    @staticmethod
    def edit_normal_tickets(save_file: core.SaveFile):
        name = core.core_data.get_gatya_item_names(save_file).get_name(20)
        save_file.normal_tickets = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "normal_tickets"),
            save_file.normal_tickets,
            core.core_data.max_value_manager.get("normal_tickets"),
        ).edit()

    @staticmethod
    def edit_100_million_ticket(save_file: core.SaveFile):
        color.ColoredText.localize("100_million_warn")
        name = core.core_data.get_gatya_item_names(save_file).get_name(212)
        save_file.hundred_million_ticket = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "100_million_tickets"),
            save_file.hundred_million_ticket,
            core.core_data.max_value_manager.get("100_million_tickets"),
        ).edit()

    @staticmethod
    def get_bannable_feature_options(feature_name: str, safe_feature_name: str) -> int:
        feature_name = core.core_data.local_manager.get_key(feature_name)
        safe_feature_name = core.core_data.local_manager.get_key(safe_feature_name)

        options = [
            core.core_data.local_manager.get_key(
                "continue_editing", feature_name=feature_name
            ),
            core.core_data.local_manager.get_key(
                "go_to_safe_feature", safer_feature_name=safe_feature_name
            ),
            core.core_data.local_manager.get_key(
                "cancel_editing", feature_name=feature_name
            ),
        ]
        option = dialog_creator.ChoiceInput(
            options,
            options,
            [],
            {"feature_name": feature_name},
            "select_an_option_to_continue",
        ).single_choice()
        if option is None:
            return 2
        option -= 1
        return option

    @staticmethod
    def edit_rare_tickets(save_file: core.SaveFile):
        color.ColoredText.localize("rare_ticket_warning")
        name = core.core_data.get_gatya_item_names(save_file).get_name(21)
        option = BasicItems.get_bannable_feature_options(
            "rare_tickets_l", "rare_ticket_trade_l"
        )
        if option == 2:
            return
        if option == 1:
            return edits.rare_ticket_trade.RareTicketTrade.rare_ticket_trade(save_file)

        original_amount = save_file.rare_tickets
        save_file.rare_tickets = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "rare_tickets"),
            save_file.rare_tickets,
            core.core_data.max_value_manager.get("rare_tickets"),
        ).edit()
        change = save_file.rare_tickets - original_amount
        core.BackupMetaData(save_file).add_managed_item(
            core.ManagedItem.from_change(change, core.ManagedItemType.RARE_TICKET)
        )

    @staticmethod
    def edit_platinum_tickets(save_file: core.SaveFile):
        color.ColoredText.localize("platinum_ticket_warning")
        name = core.core_data.get_gatya_item_names(save_file).get_name(29)
        option = BasicItems.get_bannable_feature_options(
            "platinum_tickets_l", "platinum_shards_l"
        )
        if option == 2:
            return
        if option == 1:
            return edits.basic_items.BasicItems.edit_platinum_shards(save_file)

        original_amount = save_file.platinum_tickets
        save_file.platinum_tickets = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "platinum_tickets"),
            save_file.platinum_tickets,
            core.core_data.max_value_manager.get("platinum_tickets"),
        ).edit()
        change = save_file.platinum_tickets - original_amount
        core.BackupMetaData(save_file).add_managed_item(
            core.ManagedItem.from_change(change, core.ManagedItemType.PLATINUM_TICKET)
        )

    @staticmethod
    def edit_legend_tickets(save_file: core.SaveFile):
        should_exit = not dialog_creator.YesNoInput().get_input_once(
            "legend_ticket_warning"
        )
        if should_exit:
            return
        name = core.core_data.get_gatya_item_names(save_file).get_name(145)
        original_amount = save_file.legend_tickets
        save_file.legend_tickets = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "legend_tickets"),
            save_file.legend_tickets,
            core.core_data.max_value_manager.get("legend_tickets"),
        ).edit()
        change = save_file.legend_tickets - original_amount
        core.BackupMetaData(save_file).add_managed_item(
            core.ManagedItem.from_change(change, core.ManagedItemType.LEGEND_TICKET)
        )

    @staticmethod
    def edit_platinum_shards(save_file: core.SaveFile):
        name = core.core_data.get_gatya_item_names(save_file).get_name(157)
        platinum_ticket_amount = save_file.platinum_tickets
        max_value = (
            core.core_data.max_value_manager.get("platinum_tickets")
            - platinum_ticket_amount
        ) * 10 + 9

        max_value = max(0, max_value)
        save_file.platinum_shards = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "platinum_shards"),
            save_file.platinum_shards,
            max_value,
        ).edit()

    @staticmethod
    def edit_np(save_file: core.SaveFile):
        name = core.core_data.get_gatya_item_names(save_file).get_name(7)
        save_file.np = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "np"),
            save_file.np,
            core.core_data.max_value_manager.get("np"),
        ).edit()

    @staticmethod
    def edit_leadership(save_file: core.SaveFile):
        name = core.core_data.get_gatya_item_names(save_file).get_name(105)
        save_file.leadership = dialog_creator.SingleEditor(
            BasicItems.get_name(name, "leadership"),
            save_file.leadership,
            core.core_data.max_value_manager.get("leadership"),
        ).edit()

    @staticmethod
    def edit_battle_items(save_file: core.SaveFile):
        save_file.battle_items.edit(save_file)

    @staticmethod
    def edit_battle_items_endless(save_file: core.SaveFile):
        save_file.battle_items.edit_endless_items(save_file)

    @staticmethod
    def edit_catamins(save_file: core.SaveFile):
        names_o = core.core_data.get_gatya_item_names(save_file)
        items = core.core_data.get_gatya_item_buy(save_file).get_by_category(6)
        if items is None:
            return
        names: list[str] = []
        for item in items:
            name = names_o.get_name(item.id)
            if name is None:
                name = core.core_data.local_manager.get_key(
                    "unknown_catamin_name", id=item.id
                )
            names.append(name)
        values = dialog_creator.MultiEditor.from_reduced(
            "catamins",
            names,
            save_file.catamins,
            core.core_data.max_value_manager.get("catamins"),
            group_name_localized=True,
        ).edit()
        save_file.catamins = values

    @staticmethod
    def edit_catseyes(save_file: core.SaveFile):
        names_o = core.core_data.get_gatya_item_names(save_file)
        items = core.core_data.get_gatya_item_buy(save_file).get_by_category(5)
        if items is None:
            return
        names: list[str] = []
        for item in items:
            name = names_o.get_name(item.id)
            if name is None:
                name = core.core_data.local_manager.get_key(
                    "unknown_catseye_name", id=item.id
                )
            names.append(name)

        values = dialog_creator.MultiEditor.from_reduced(
            "catseyes",
            names,
            save_file.catseyes,
            core.core_data.max_value_manager.get("catseyes"),
            group_name_localized=True,
        ).edit()
        save_file.catseyes = values

    @staticmethod
    def edit_treasure_chests(save_file: core.SaveFile):
        names_o = core.core_data.get_gatya_item_names(save_file)
        items = core.core_data.get_gatya_item_buy(save_file).get_by_category(
            GatyaItemCategory.TREASURE_CHESTS
        )
        if items is None:
            return
        names: list[str] = []
        for item in items[: len(save_file.treasure_chests)]:
            name = names_o.get_name(item.id)
            if name is None:
                name = core.core_data.local_manager.get_key(
                    "unknown_treasure_chest_name", id=item.id
                )
            names.append(name)

        values = dialog_creator.MultiEditor.from_reduced(
            "treasure_chests",
            names,
            save_file.treasure_chests,
            core.core_data.max_value_manager.get("treasure_chests"),
            group_name_localized=True,
        ).edit()
        save_file.treasure_chests = values

    @staticmethod
    def edit_catfruit(save_file: core.SaveFile):
        names = core.Matatabi(save_file).get_names()
        if names is None:
            return
        new_names: list[str] = []
        for i, name in enumerate(names):
            if name is None:
                name = core.core_data.local_manager.get_key(
                    "unknown_catfruit_name", id=i
                )
            new_names.append(name)
        names = new_names

        extra = len(save_file.catfruit) - len(names)
        if extra > 0:
            for i in range(extra):
                names.append(
                    core.core_data.local_manager.get_key(
                        "unknown_catfruit_name", id=i + len(names)
                    )
                )

        if save_file.game_version < 110400:
            max_value = core.core_data.max_value_manager.get_old("catfruit")
            cumulative_max = True
        else:
            max_value = core.core_data.max_value_manager.get_new("catfruit")
            cumulative_max = False

        names = names[: len(save_file.catfruit)]

        values = dialog_creator.MultiEditor.from_reduced(
            "catfruit",
            names,
            save_file.catfruit,
            max_value,
            group_name_localized=True,
            cumulative_max=cumulative_max,
        ).edit()
        save_file.catfruit = values

    @staticmethod
    def set_restart_pack(save_file: core.SaveFile):
        save_file.restart_pack = 1
        name = core.core_data.get_gatya_item_names(save_file).get_name(123)
        color.ColoredText.localize("value_gave", name=name)

    @staticmethod
    def edit_inquiry_code(save_file: core.SaveFile):
        should_exit = not dialog_creator.YesNoInput().get_input_once(
            "inquiry_code_warning"
        )
        if should_exit:
            return
        item_name = save_file.get_localizable().get("autoSave_txt5")
        save_file.inquiry_code = dialog_creator.StringEditor(
            BasicItems.get_name(item_name, "inquiry_code"),
            save_file.inquiry_code,
        ).edit()

    @staticmethod
    def edit_password_refresh_token(save_file: core.SaveFile):
        should_exit = not dialog_creator.YesNoInput().get_input_once(
            "password_refresh_token_warning"
        )
        if should_exit:
            return
        save_file.password_refresh_token = dialog_creator.StringEditor(
            "password_refresh_token",
            save_file.password_refresh_token,
            item_localized=True,
        ).edit()

    @staticmethod
    def edit_scheme_items(save_file: core.SaveFile):
        save_file.scheme_items.edit(save_file)

    @staticmethod
    def edit_engineers(save_file: core.SaveFile):
        save_file.ototo.edit_engineers(save_file)

    @staticmethod
    def edit_base_materials(save_file: core.SaveFile):
        save_file.ototo.base_materials.edit_base_materials(save_file)

    @staticmethod
    def edit_rare_gatya_seed(save_file: core.SaveFile):
        save_file.gatya.edit_rare_gatya_seed()

    @staticmethod
    def edit_normal_gatya_seed(save_file: core.SaveFile):
        save_file.gatya.edit_normal_gatya_seed()

    @staticmethod
    def edit_event_gatya_seed(save_file: core.SaveFile):
        save_file.gatya.edit_event_gatya_seed()

    @staticmethod
    def edit_unlocked_slots(save_file: core.SaveFile):
        save_file.lineups.edit_unlocked_slots()

    @staticmethod
    def edit_labyrinth_medals(save_file: core.SaveFile):
        names_o = core.core_data.get_gatya_item_names(save_file)
        items = core.core_data.get_gatya_item_buy(save_file).get_by_category(11)
        if items is None:
            return
        names: list[str] = []
        for item in items:
            name = names_o.get_name(item.id)
            if name is None:
                name = core.core_data.local_manager.get_key(
                    "unknown_labyrinth_medal_name", id=item.id
                )
            names.append(name)

        values = dialog_creator.MultiEditor.from_reduced(
            "labyrinth_medals",
            names,
            save_file.labyrinth_medals,
            core.core_data.max_value_manager.get("labyrinth_medals"),
            group_name_localized=True,
        ).edit()
        save_file.labyrinth_medals = values

    @staticmethod
    def edit_special_skills(save_file: core.SaveFile):
        save_file.special_skills.edit(save_file)

    @staticmethod
    def unlock_equip_menu(save_file: core.SaveFile):
        save_file.unlock_equip_menu()
        color.ColoredText.localize("equip_menu_unlocked")

    @staticmethod
    def allow_filibuster_stage_reclearing(save_file: core.SaveFile):
        save_file.filibuster_stage_enabled = True
        save_file.filibuster_stage_id = random.randint(0, 47)
        color.ColoredText.localize("filibuster_stage_reclearing_allowed")


================================================
FILE: src/bcsfe/cli/edits/cat_editor.py
================================================
from __future__ import annotations
import enum
from typing import Any, Callable

from bcsfe import core
from bcsfe.cli import color, dialog_creator


class SelectMode(enum.Enum):
    AND = 0
    OR = 1
    REPLACE = 2


class CatEditor:
    def __init__(self, save_file: core.SaveFile):
        self.save_file = save_file

    def get_current_cats(self):
        return self.save_file.cats.get_unlocked_cats()

    def get_non_unlocked_cats(self):
        return self.save_file.cats.get_non_unlocked_cats()

    def get_non_gacha_cats(self):
        return self.save_file.cats.get_non_gacha_cats(self.save_file)

    def filter_cats(self, cats: list[core.Cat]) -> list[core.Cat]:
        unlocked_cats = self.get_current_cats()
        return [cat for cat in cats if cat in unlocked_cats]

    def get_cats_rarity(self, rarity: int) -> list[core.Cat]:
        return self.save_file.cats.get_cats_rarity(self.save_file, rarity)

    def get_cats_name(self, name: str) -> list[core.Cat]:
        return self.save_file.cats.get_cats_name(self.save_file, name)

    def get_cats_obtainable(self) -> list[core.Cat] | None:
        return self.save_file.cats.get_cats_obtainable(self.save_file)

    def get_cats_unobtainable(self) -> list[core.Cat] | None:
        return self.save_file.cats.get_cats_non_obtainable(self.save_file)

    def get_cats_gatya_banner(self, gatya_id: int) -> list[core.Cat] | None:
        return self.save_file.cats.get_cats_gatya_banner(self.save_file, gatya_id)

    def print_selected_cats(self, current_cats: list[core.Cat]):
        if len(current_cats) > 50:
            color.ColoredText.localize("total_selected_cats", total=len(current_cats))
        else:
            for cat in current_cats:
                names = cat.get_names_cls(self.save_file)
                if not names:
                    names = [str(cat.id)]
                color.ColoredText.localize("selected_cat", id=cat.id, name=names[0])

    def select(
        self, current_cats: list[core.Cat] | None = None, finish_option: bool = True
    ) -> tuple[list[core.Cat], bool]:
        if current_cats is None:
            current_cats = []
        options: dict[str, Callable[[], Any]] = {
            "select_cats_all": self.save_file.cats.get_all_cats,
            "select_cats_current": self.get_current_cats,
            "select_cats_obtainable": self.get_cats_obtainable,
            "select_cats_id": self.select_id,
            "select_cats_name": self.select_name,
            "select_cats_rarity": self.select_rarity,
            "select_cats_gatya_banner": self.select_gatya_banner,
            "select_cats_not_unlocked": self.get_non_unlocked_cats,
            "select_cats_not_obtainable": self.get_cats_unobtainable,
            "select_cats_non_gatya": self.get_non_gacha_cats,
            "select_cats_game_version": self.select_cats_game_version,
        }
        if finish_option:
            options["finish"] = lambda: None
        option_id = dialog_creator.ChoiceInput(
            list(options), list(options), [], {}, "select_cats", True
        ).single_choice()
        if option_id is None:
            return current_cats, False
        option_id -= 1

        if option_id == len(options) - 1 and finish_option:
            return current_cats, True

        func = options[list(options)[option_id]]
        new_cats = func()

        if new_cats is None:
            return current_cats, False

        if current_cats:
            mode_id = dialog_creator.IntInput().get_basic_input_locale("and_mode_q", {})
            if mode_id is None:
                mode = SelectMode.OR
            elif mode_id == 1:
                mode = SelectMode.AND
            elif mode_id == 2:
                mode = SelectMode.OR
            elif mode_id == 3:
                mode = SelectMode.REPLACE
            else:
                mode = SelectMode.OR
        else:
            mode = SelectMode.OR

        if mode == SelectMode.AND:
            return list(set(current_cats) & set(new_cats)), False
        if mode == SelectMode.OR:
            return list(set(current_cats) | set(new_cats)), False
        if mode == SelectMode.REPLACE:
            return new_cats, False
        return new_cats, False

    def select_id(self) -> list[core.Cat] | None:
        cat_ids = dialog_creator.RangeInput(
            len(self.save_file.cats.cats) - 1
        ).get_input_locale("enter_cat_ids", {})
        if cat_ids is None:
            return None
        return self.save_file.cats.get_cats_by_ids(cat_ids)

    def select_cats_game_version(self) -> list[core.Cat] | None:
        unitbuy = core.UnitBuy(self.save_file)
        if unitbuy.unit_buy is None:
            return None

        versions_set: set[int] = set()
        for cat in unitbuy.unit_buy:
            if cat.game_version == -1:
                continue
            versions_set.add(cat.game_version)

        if not versions_set:
            return None

        versions = list(versions_set)
        versions.sort()

        color.ColoredText.localize("possible_gvs")

        cur_major_v = -1
        for version in versions:
            gv = core.GameVersion(version)
            major_v = gv.get_parts()[0]
            if major_v != cur_major_v:
                if cur_major_v != -1:
                    print()
                cur_major_v = major_v
            else:
                color.ColoredText(", ", end="")
            color.ColoredText(f"<@t>{gv.format()}</>", end="")

        print()

        usr_input = dialog_creator.StringInput().get_input_locale("select_gv")
        if usr_input is None:
            return None
        chunks = usr_input.split(" ")

        versions_selected: list[int] = []
        for chunk in chunks:
            parts = chunk.split("-")
            if len(parts) == 2:
                min = parts[0]
                max = parts[1]

                v1 = core.GameVersion.from_string(min)
                v2 = core.GameVersion.from_string(max)

                for v in range(v1.game_version, v2.game_version + 1):
                    versions_selected.append(v)
            else:
                v = core.GameVersion.from_string(chunk)
                versions_selected.append(v.game_version)

        valid_versions: set[int] = set()
        for version in versions_selected:
            if version in versions_set:
                valid_versions.add(version)

        if not valid_versions:
            color.ColoredText.localize("no_valid_gvs_entered")

        cats: list[core.Cat] = []
        for cat in self.save_file.cats.cats:
            row = unitbuy.get_unit_buy(cat.id)
            if row is None:
                continue
            if row.game_version in valid_versions:
                cats.append(cat)

        return cats

    def select_rarity(self) -> list[core.Cat] | None:
        rarity_names = self.save_file.cats.get_rarity_names(self.save_file)
        rarity_ids, _ = dialog_creator.ChoiceInput(
            rarity_names, rarity_names, [], {}, "select_rarity"
        ).multiple_choice()
        if rarity_ids is None:
            return None
        cats: list[core.Cat] = []
        for rarity_id in rarity_ids:
            rarity_cats = self.get_cats_rarity(rarity_id)
            cats = list(set(cats + rarity_cats))
        return cats

    def select_name(self) -> list[core.Cat] | None:
        usr_name = dialog_creator.StringInput().get_input_locale("enter_name", {})
        if usr_name is None:
            return []
        cats = self.get_cats_name(usr_name)
        if not cats:
            color.ColoredText.localize("no_cats_found_name", name=usr_name)
            return None
        cat_names: list[str] = []
        cat_list: list[core.Cat] = []
        for cat in cats:
            names = cat.get_names_cls(self.save_file)
            if not names:
                names = [str(cat.id)]
            for name in names:
                if usr_name.lower() in name.lower():
                    cat_names.append(name)
                    cat_list.append(cat)
                    break
        if len(cat_names) == 1:
            color.ColoredText(f"<@t>{cat_names[0]}</>")
        cat_option_ids, _ = dialog_creator.ChoiceInput(
            cat_names, cat_names, [], {}, "select_name"
        ).multiple_choice()
        if cat_option_ids is None:
            return None
        cats_selected: list[core.Cat] = []
        for cat_option_id in cat_option_ids:
            cats_selected.append(cat_list[cat_option_id])
        return cats_selected

    def select_obtainable(self) -> list[core.Cat] | None:
        return self.get_cats_obtainable()

    def select_gatya_banner_name(self) -> list[int] | None:

        filter_down = dialog_creator.YesNoInput().get_input_once("filter_down_q_gatya")
        if filter_down is None:
            return None

        all_names = core.GatyaInfos(self.save_file).get_all_names()
        ids = list(all_names.keys())
        ids.sort()
        names: list[str] = []
        for id in ids:
            names.append(all_names[id])
        new_names: list[str] = []
        new_ids: list[int] = []

        unknown_name = core.core_data.local_manager.get_key("unknown_banner")

        if filter_down:
            ids.reverse()
            for id in ids:
                name = all_names[id]
                if name in new_names or name == unknown_name:
                    continue
                new_names.append(name)
                new_ids.append(id)
            new_ids.reverse()
            new_names.reverse()
        else:
            new_names = names
            new_ids = ids

        ids = new_ids

        formatted_names: list[str] = []

        for name in new_names:
            formatted_name = core.core_data.local_manager.get_key(
                "banner_txt", name=name
            )
            formatted_names.append(formatted_name)
        gatya_option_ids, _ = dialog_creator.ChoiceInput.from_reduced(
            formatted_names,
            ints=ids,
            dialog="select_gatya_banner",
            start_index=0,
        ).multiple_choice(False)
        if gatya_option_ids is None:
            return None
        gatya_ids: list[int] = []
        for gatya_option_id in gatya_option_ids:
            gatya_ids.append(ids[gatya_option_id])

        return gatya_ids

    def select_gatya_banner(self) -> list[core.Cat] | None:
        gset = self.save_file.gatya.read_gatya_data_set(self.save_file).gatya_data_set
        if gset is None:
            return None

        by_id = dialog_creator.ChoiceInput.from_reduced(
            ["by_id", "by_name"], dialog="gatya_by_id_q"
        ).single_choice()
        if by_id is None:
            return None

        if by_id == 1:
            gatya_ids = dialog_creator.RangeInput(len(gset) - 1).get_input_locale(
                "select_gatya_banner", {}
            )
        else:
            gatya_ids = self.select_gatya_banner_name()
        if gatya_ids is None:
            return None
        cats: list[core.Cat] = []
        for gatya_id in gatya_ids:
            gatya_cats = self.get_cats_gatya_banner(gatya_id)
            if gatya_cats is None:
                continue
            cats = list(set(cats + gatya_cats))
        return cats

    def unlock_cats(self, cats: list[core.Cat]):
        cats = self.get_save_cats(cats)
        for cat in cats:
            cat.unlock(self.save_file)
        color.ColoredText.localize("unlock_success")

    def remove_cats(self, cats: list[core.Cat]):
        reset = core.core_data.config.get_bool(core.ConfigKey.RESET_CAT_DATA)
        cats = self.get_save_cats(cats)
        for cat in cats:
            cat.remove(reset=reset, save_file=self.save_file)
        color.ColoredText.localize("remove_success")

    def get_save_cats(self, cats: list[core.Cat]):
        ct_cats: list[core.Cat] = []
        for cat in cats:
            ct = self.save_file.cats.get_cat_by_id(cat.id)
            if ct is None:
                continue
            ct_cats.append(ct)
        return ct_cats

    def true_form_cats(self, cats: list[core.Cat], force: bool = False):
        cats = self.get_save_cats(cats)
        set_current_forms = core.core_data.config.get_bool(
            core.ConfigKey.SET_CAT_CURRENT_FORMS
        )
        self.save_file.cats.true_form_cats(
            self.save_file, cats, force, set_current_forms
        )
        color.ColoredText.localize("true_form_success")

    def fourth_form_cats(self, cats: list[core.Cat], force: bool = False):
        cats = self.get_save_cats(cats)
        set_current_forms = core.core_data.config.get_bool(
            core.ConfigKey.SET_CAT_CURRENT_FORMS
        )
        self.save_file.cats.fourth_form_cats(
            self.save_file, cats, force, set_current_forms
        )
        color.ColoredText.localize("fourth_form_success")

    def remove_true_form_cats(self, cats: list[core.Cat]):
        cats = self.get_save_cats(cats)
        for cat in cats:
            cat.remove_true_form()
        color.ColoredText.localize("remove_true_form_success")

    def remove_fourth_form_cats(self, cats: list[core.Cat]):
        cats = self.get_save_cats(cats)
        for cat in cats:
            cat.remove_fourth_form()
        color.ColoredText.localize("remove_fourth_form_success")

    def upgrade_cats(self, cats: list[core.Cat]):
        cats = self.get_save_cats(cats)
        if not cats:
            return
        if len(cats) == 1:
            option_id = 0
        else:
            options: list[str] = [
                "upgrade_individual",
                "upgrade_all",
            ]
            option_id = dialog_creator.ChoiceInput(
                options, options, [], {}, "upgrade_cats_select_mod", True
            ).single_choice()
            if option_id is None:
                return
            option_id -= 1
        success = False
        if option_id == 0:
            for cat in cats:
                names = cat.get_names_cls(self.save_file)
                if not names:
                    names = [str(cat.id)]
                color.ColoredText.localize(
                    "selected_cat_upgrades",
                    name=names[0],
                    id=cat.id,
                    base_level=cat.upgrade.base + 1,
                    plus_level=cat.upgrade.plus,
                )
                power_up = core.PowerUpHelper(cat, self.save_file)
                upgrade, should_exit = core.Upgrade.get_user_upgrade(
                    power_up.get_max_possible_base() - 1,
                    power_up.get_max_possible_plus(),
                )
                if should_exit:
                    return
                if upgrade is not None:
                    power_up.reset_upgrade()
                    power_up.upgrade_by(upgrade.base)
                    cat.set_upgrade(self.save_file, upgrade, True)
                    color.ColoredText.localize(
                        "selected_cat_upgraded",
                        name=names[0],
                        id=cat.id,
                        base_level=cat.upgrade.base + 1,
                        plus_level=cat.upgrade.plus,
                    )
                    success = True
        else:
            power_up = core.PowerUpHelper(cats[0], self.save_file)
            upgrade, should_exit = core.Upgrade.get_user_upgrade(
                power_up.get_max_max_base_upgrade_level() - 1,
                power_up.get_max_max_plus_upgrade_level(),
            )
            if upgrade is None or should_exit:
                return
            success = True
            for cat in cats:
                power_up = core.PowerUpHelper(cat, self.save_file)
                power_up.reset_upgrade()
                power_up.upgrade_by(upgrade.base)
                cat.set_upgrade(self.save_file, upgrade, True)
        if success:
            color.ColoredText.localize("upgrade_success")

    def remove_talents_cats(self, cats: list[core.Cat]):
        for cat in cats:
            if cat.talents is None:
                continue
            for talent in cat.talents:
                talent.level = 0
        color.ColoredText.localize("talents_remove_success")

    def unlock_cat_guide(self, cats: list[core.Cat]):
        for cat in cats:
            if core.core_data.config.get_bool(core.ConfigKey.UNLOCK_CAT_ON_EDIT):
                cat.unlock(self.save_file)
            cat.catguide_collected = True
        color.ColoredText.localize("unlock_cat_guide_success")

    def remove_cat_guide(self, cats: list[core.Cat]):
        for cat in cats:
            cat.catguide_collected = False
        color.ColoredText.localize("remove_cat_guide_success")

    def upgrade_talents_cats(self, cats: list[core.Cat]):
        cats = self.get_save_cats(cats)
        if not cats:
            return
        gdg = core.core_data.get_game_data_getter(self.save_file)
        is_good_version = gdg.does_save_version_match(self.save_file)
        if not is_good_version:
            data_version = gdg.version
            if data_version is None:
                color.ColoredText.localize("no_data_version")
                return
            color.ColoredText.localize(
                "talents_version_warning",
                save_version=self.save_file.game_version.to_string(),
                data_version=data_version,
            )
            should_stay = dialog_creator.YesNoInput().get_input_once("continue_q")
            if not should_stay:
                return

        if len(cats) == 1:
            option_id = 0
        else:
            options: list[str] = [
                "talents_individual",
                "talents_all",
            ]
            option_id = dialog_creator.ChoiceInput(
                options, options, [], {}, "upgrade_talents_select_mod", True
            ).single_choice()
            if option_id is None:
                return
            option_id -= 1

        talent_data = self.save_file.cats.read_talent_data(self.save_file)
        if talent_data is None:
            return
        if option_id == 0:
            for cat in cats:
                if cat.talents is None:
                    continue
                names = cat.get_names_cls(self.save_file)
                if not names:
                    names = [str(cat.id)]
                color.ColoredText.localize(
                    "selected_cat",
                    name=names[0],
                    id=cat.id,
                )
                data = talent_data.get_cat_talents(cat)
                if data is None:
                    color.ColoredText.localize("no_talent_data", id=cat.id)
                    continue
                if core.core_data.config.get_bool(core.ConfigKey.UNLOCK_CAT_ON_EDIT):
                    cat.unlock(self.save_file)
                talent_names, max_levels, current_levels, ids = data
                values = dialog_creator.MultiEditor.from_reduced(
                    "talents",
                    talent_names,
                    current_levels,
                    max_levels,
                    group_name_localized=True,
                ).edit()
                current_levels = values
                for i, id in enumerate(ids):
                    talent = cat.get_talent_from_id(id)
                    if talent is None:
                        continue
                    talent.level = current_levels[i]
        else:
            for cat in cats:
                if cat.talents is None:
                    continue
                data = talent_data.get_cat_talents(cat)
                if data is None:
                    continue
                if core.core_data.config.get_bool(core.ConfigKey.UNLOCK_CAT_ON_EDIT):
                    cat.unlock(self.save_file)
                talent_names, max_levels, current_levels, ids = data
                for i, id in enumerate(ids):
                    talent = cat.get_talent_from_id(id)
                    if talent is None:
                        continue
                    talent.level = max_levels[i]
        color.ColoredText.localize("talents_success")

    @staticmethod
    def edit_cats(save_file: core.SaveFile):
        cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        while True:
            should_exit, current_cats = cat_editor.run_edit_cats(current_cats)
            if should_exit:
                break

    @staticmethod
    def unlock_remove_cats_run(
        save_file: core.SaveFile,
        current_cats: list[core.Cat] | None = None,
        cat_editor: CatEditor | None = None,
    ):
        if cat_editor is None or current_cats is None:
            cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        choice = dialog_creator.ChoiceInput(
            ["unlock_cats", "remove_cats"],
            ["unlock_cats", "remove_cats"],
            [],
            {},
            "unlock_remove_q",
            True,
            remove_alias=True,
        ).single_choice()
        if choice is None:
            return
        choice -= 1
        if choice == 0:
            cat_editor.unlock_cats(current_cats)
        elif choice == 1:
            cat_editor.remove_cats(current_cats)
        CatEditor.set_rank_up_sale(save_file)

    @staticmethod
    def true_form_remove_form_cats_run(
        save_file: core.SaveFile,
        current_cats: list[core.Cat] | None = None,
        cat_editor: CatEditor | None = None,
    ):
        if cat_editor is None or current_cats is None:
            cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        choice = dialog_creator.ChoiceInput.from_reduced(
            ["true_form_cats", "remove_true_form_cats"],
            dialog="true_form_remove_form_q",
            single_choice=True,
        ).single_choice()
        if choice is None:
            return
        choice -= 1
        if choice == 0:
            cat_editor.true_form_cats(current_cats)
        elif choice == 1:
            cat_editor.remove_true_form_cats(current_cats)

    @staticmethod
    def fourth_form_remove_form_cats_run(
        save_file: core.SaveFile,
        current_cats: list[core.Cat] | None = None,
        cat_editor: CatEditor | None = None,
    ):
        if cat_editor is None or current_cats is None:
            cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        choice = dialog_creator.ChoiceInput.from_reduced(
            ["fourth_form_cats", "remove_fourth_form_cats"],
            dialog="fourth_form_remove_form_q",
            single_choice=True,
        ).single_choice()
        if choice is None:
            return
        choice -= 1
        if choice == 0:
            cat_editor.fourth_form_cats(current_cats)
        elif choice == 1:
            cat_editor.remove_fourth_form_cats(current_cats)

    @staticmethod
    def force_true_form_cats_run(save_file: core.SaveFile):
        color.ColoredText.localize("force_true_form_cats_warning")
        cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        cat_editor.true_form_cats(current_cats, force=True)

    @staticmethod
    def force_fourth_form_cats_run(save_file: core.SaveFile):
        color.ColoredText.localize("force_fourth_form_cats_warning")
        cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        cat_editor.fourth_form_cats(current_cats, force=True)

    @staticmethod
    def upgrade_cats_run(save_file: core.SaveFile):
        cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        cat_editor.upgrade_cats(current_cats)
        CatEditor.set_rank_up_sale(save_file)

    @staticmethod
    def upgrade_talents_remove_talents_cats_run(
        save_file: core.SaveFile,
        current_cats: list[core.Cat] | None = None,
        cat_editor: CatEditor | None = None,
    ):
        if cat_editor is None or current_cats is None:
            cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        choice = dialog_creator.ChoiceInput(
            ["upgrade_talents_cats", "remove_talents_cats"],
            ["upgrade_talents_cats", "remove_talents_cats"],
            [],
            {},
            "upgrade_talents_remove_talents_q",
            True,
        ).single_choice()
        if choice is None:
            return
        choice -= 1
        if choice == 0:
            cat_editor.upgrade_talents_cats(current_cats)
        elif choice == 1:
            cat_editor.remove_talents_cats(current_cats)

    @staticmethod
    def unlock_cat_guide_remove_guide_run(
        save_file: core.SaveFile,
        current_cats: list[core.Cat] | None = None,
        cat_editor: CatEditor | None = None,
    ):
        if cat_editor is None or current_cats is None:
            cat_editor, current_cats = CatEditor.from_save_file(save_file)
        if cat_editor is None:
            return
        choice = dialog_creator.ChoiceInput(
            ["unlock_cat_guide", "remove_cat_guide"],
            ["unlock_cat_guide", "remove_cat_guide"],
            [],
            {},
            "unlock_cat_guide_remove_guide_q",
            True,
        ).single_choice()
        if choice is None:
            return
        choice -= 1
        if choice == 0:
            cat_editor.unlock_cat_guide(current_cats)
        elif choice == 1:
            cat_editor.remove_cat_guide(current_cats)

    @staticmethod
    def from_save_file(
        save_file: core.SaveFile,
    ) -> tuple[CatEditor | None, list[core.Cat]]:
        cat_editor = CatEditor(save_file)
        stop = False
        cats = []
        while not stop:
            current_cats, finished = cat_editor.select(cats)
            cats = current_cats
            cat_editor.print_selected_cats(cats)
            if finished:
                stop = True
                continue
            finished = dialog_creator.YesNoInput().get_input_once(
                "finished_cats_selection"
            )
            if finished is None:
                return None, []
            stop = finished
        return cat_editor, cats

    def run_edit_cats(
        self,
        cats: list[core.Cat],
    ) -> tuple[bool, list[core.Cat]]:
        self.print_selected_cats(cats)
        options: list[str] = [
            "select_cats_again",
            "unlock_remove_cats",
            "upgrade_cats",
            "true_form_remove_form_cats",
            "force_true_form_cats",
            "fourth_form_remove_form_cats",
            "force_fourth_form_cats",
            "upgrade_talents_remove_talents_cats",
            "unlock_remove_cat_guide",
            "finish_edit_cats",
        ]
        option_id = dialog_creator.ChoiceInput(
            options,
            options,
            [],
            {},
            "select_edit_cats_option",
            True,
            remove_alias=True,
        ).single_choice()
        if option_id is None:
            return False, cats
        option_id -= 1
        if option_id == 0:
            cats_, _ = self.select(cats, False)
            cats = cats_
        elif option_id == 1:
            self.unlock_remove_cats_run(self.save_file, cats, self)
        elif option_id == 2:
            self.upgrade_cats(cats)
        elif option_id == 3:
            self.true_form_remove_form_cats_run(self.save_file, cats, self)
        elif option_id == 4:
            color.ColoredText.localize("force_true_form_cats_warning")
            self.true_form_cats(cats, force=True)
        elif option_id == 5:
            self.fourth_form_remove_form_cats_run(self.save_file, cats, self)
        elif option_id == 6:
            color.ColoredText.localize("force_fourth_form_cats_warning")
            self.fourth_form_cats(cats, force=True)
        elif option_id == 7:
            self.upgrade_talents_remove_talents_cats_run(self.save_file, cats, self)
        elif option_id == 8:
            self.unlock_cat_guide_remove_guide_run(self.save_file, cats, self)
        CatEditor.set_rank_up_sale(self.save_file)
        if option_id == 9:
            return True, cats
        return False, cats

    @staticmethod
    def set_rank_up_sale(save_file: core.SaveFile):
        save_file.rank_up_sale_value = 0x7FFFFFFF


================================================
FILE: src/bcsfe/cli/edits/clear_tutorial.py
================================================
from __future__ import annotations
from bcsfe import core
from bcsfe.cli import color


def clear_tutorial(
    save_file: core.SaveFile, display_already_cleared: bool = True
):
    core.StoryChapters.clear_tutorial(save_file)
    if display_already_cleared:
        color.ColoredText.localize("tutorial_cleared")


================================================
FILE: src/bcsfe/cli/edits/enemy_editor.py
================================================
from __future__ import annotations
from typing import Any
from bcsfe import core
from bcsfe.cli import color, dialog_creator
from bcsfe.cli.edits.cat_editor import SelectMode
from bcsfe.core.game.battle.enemy import EnemyNames


class EnemyEditor:
    def __init__(self, save_file: core.SaveFile) -> None:
        self.save_file = save_file

    def unlock_enemy_guide(self, enemies: list[core.Enemy]):
        for enemy in enemies:
            enemy.unlock_enemy_guide(self.save_file)

        color.ColoredText.localize("unlock_enemy_guide_success")

    def remove_enemy_guide(self, enemies: list[core.Enemy]):
        for enemy in enemies:
            enemy.reset_enemy_guide(self.save_file)

        color.ColoredText.localize("remove_enemy_guide_success")

    def print_selected_enemies(self, enemies: list[core.Enemy]):
        if not enemies:
            return
        if len(enemies) > 50:
            color.ColoredText.localize("total_selected_enemies", total=len(enemies))
        else:
            for enemy in enemies:
                color.ColoredText.localize(
                    "selected_enemy",
                    id=enemy.id,
                    name=enemy.get_name(self.save_file),
                )

    def select(self, current_enemies: list[core.Enemy] | None):
        if current_enemies is None:
            current_enemies = []
        self.print_selected_enemies(current_enemies)

        options: dict[str, Any] = {
            "select_enemies_valid": self.get_all_valid_enemies,
            "select_enemies_all": self.get_all_enemies,
            "select_enemies_id": self.select_id,
            "select_enemies_name": self.select_name,
            "select_enemies_invalid": self.get_all_invalid_enemies,
        }
        option_id = dialog_creator.ChoiceInput.from_reduced(
            list(options), dialog="select_enemies", single_choice=True
        ).single_choice()
        if option_id is None:
            return current_enemies
        option_id -= 1

        func = options[list(options)[option_id]]
        new_enemies = func()
        if new_enemies is None:
            return None

        if current_enemies:
            mode_id = dialog_creator.IntInput().get_basic_input_locale("and_mode_q", {})
            if mode_id is None:
                mode = SelectMode.OR
            elif mode_id == 1:
                mode = SelectMode.AND
            elif mode_id == 2:
                mode = SelectMode.OR
            elif mode_id == 3:
                mode = SelectMode.REPLACE
            else:
                mode = SelectMode.OR
        else:
            mode = SelectMode.OR

        if mode == SelectMode.AND:
            return [enemy for enemy in new_enemies if enemy in current_enemies]
        if mode == SelectMode.OR:
            return list(set(current_enemies + new_enemies))
        if mode == SelectMode.REPLACE:
            return new_enemies
        return new_enemies

    def get_all_enemies(self) -> list[core.Enemy]:
        enemies: list[core.Enemy] = []
        for i in range(len(self.save_file.enemy_guide)):
            enemies.append(core.Enemy(i))
        return enemies

    def get_all_valid_enemies(self) -> list[core.Enemy] | None:
        valid_ids = core.EnemyDictionary(self.save_file).get_valid_enemies()
        if valid_ids is None:
            return None

        return [core.Enemy(id) for id in valid_ids]

    def get_all_invalid_enemies(self) -> list[core.Enemy] | None:
        invalid_ids = core.EnemyDictionary(self.save_file).get_invalid_enemies(
            len(self.save_file.enemy_guide)
        )
        if invalid_ids is None:
            return None

        return [core.Enemy(id) for id in invalid_ids]

    def select_id(self) -> list[core.Enemy] | None:
        enemy_ids = dialog_creator.RangeInput(
            len(self.save_file.enemy_guide) - 1
        ).get_input_locale("enter_enemy_ids", {})
        if enemy_ids is None:
            return None
        enemy_ids = [enemy_id - 2 for enemy_id in enemy_ids]
        return self.get_enemies_by_id(enemy_ids)

    def get_enemies_by_id(self, ids: list[int]) -> list[core.Enemy]:
        enemies: list[core.Enemy] = []
        for enemy in self.get_all_enemies():
            if enemy.id in ids:
                enemies.append(enemy)
        return enemies

    def select_name(self) -> list[core.Enemy] | None:
        usr_name = dialog_creator.StringInput().get_input_locale("enter_enemy_name", {})
        if usr_name is None:
            return None
        enemies = self.get_enemies_by_name(usr_name)
        if not enemies:
            color.ColoredText.localize("enemy_not_found_name", name=usr_name)
            return None

        enemy_names = [enemy.get_name(self.save_file) for enemy in enemies]
        new_enemy_names: list[str] = []
        for enemy_name in enemy_names:
            if enemy_name is None:
                return None

            new_enemy_names.append(enemy_name)

        enemy_option_ids, _ = dialog_creator.ChoiceInput.from_reduced(
            new_enemy_names, dialog="select_enemies", single_choice=False
        ).multiple_choice()
        if enemy_option_ids is None:
            return None
        enemies_selected: list[core.Enemy] = []
        for enemy_option_id in enemy_option_ids:
            enemies_selected.append(enemies[enemy_option_id])
        return enemies_selected

    def get_enemies_by_name(self, name: str) -> list[core.Enemy]:
        enemies: list[core.Enemy] = []
        for enemy in self.get_all_enemies():
            enemy_name = enemy.get_name(self.save_file)
            if enemy_name is None:
                continue
            if name.lower() in enemy_name.lower():
                enemies.append(enemy)
        return enemies

    @staticmethod
    def from_save_file(
        save_file: core.SaveFile,
    ) -> tuple[EnemyEditor | None, list[core.Enemy]]:
        enemy_editor = EnemyEditor(save_file)
        current_enemies = enemy_editor.select([])
        if current_enemies is None:
            return None, []
        return enemy_editor, current_enemies

    @staticmethod
    def edit_enemy_guide(
        save_file: core.SaveFile,
        current_enemies: list[core.Enemy] | None = None,
        enemy_editor: EnemyEditor | None = None,
    ):
        if enemy_editor is None or current_enemies is None:
            enemy_editor, current_enemies = EnemyEditor.from_save_file(save_file)
        if enemy_editor is None or not current_enemies:
            return

        choice = dialog_creator.ChoiceInput.from_reduced(
            ["unlock_enemy_guide", "remove_enemy_guide"],
            dialog="edit_enemy_guide",
            single_choice=True,
        ).single_choice()
        if choice is None:
            return
        choice -= 1
        if choice == 0:
            enemy_editor.unlock_enemy_guide(current_enemies)
        elif choice == 1:
            enemy_editor.remove_enemy_guide(current_enemies)


================================================
FILE: src/bcsfe/cli/edits/event_tickets.py
================================================
from __future__ import annotations
from bcsfe import cli, core
from bcsfe.core.game.catbase.gatya import GatyaEventType
from bcsfe.core.server.event_data import split_hhmm, split_yyyymmdd


class EventTickets:
    def __init__(self, save_file: core.SaveFile):
        self.save_file = save_file
        self.gatya_item_buy = core.core_data.get_gatya_item_buy(self.save_file)
        self.gatya_item_names = core.core_data.get_gatya_item_names(self.save_file)
        self.gatya_option_n = core.GatyaDataOption.read(
            self.save_file, GatyaEventType.NORMAL
        )
        self.gatya_option_r = core.GatyaDataOption.read(
            self.save_file, GatyaEventType.RARE
        )
        self.gatya_option_e = core.GatyaDataOption.read(
            self.save_file, GatyaEventType.EVENT
        )

        cli.color.ColoredText.localize("downloading_gatya_data")
        temp_save_file = core.SaveFile(cc=save_file.cc, gv=save_file.game_version)
        gatya_event_data = core.ServerHandler(temp_save_file).download_gatya_data()

        if gatya_event_data is None:
            cli.color.ColoredText.localize("download_gatya_data_fail")
            self.gatya_event_data = None
        else:
            cli.color.ColoredText.localize("download_gatya_data_success")
            self.gatya_event_data = core.ServerGatyaData.from_data(gatya_event_data)

    @staticmethod
    def edit(save_file: core.SaveFile):
        event_tickets = EventTickets(save_file)

        if event_tickets.gatya_event_data is None:
            return

        event_ticket_items: list[
            tuple[
                core.ServerGatyaDataItem, core.ServerGatyaDataSet, core.GatyaItemBuyItem
            ]
        ] = []

        if (
            event_tickets.gatya_option_n is None
            or event_tickets.gatya_option_r is None
            or event_tickets.gatya_option_e is None
        ):
            return

        for item in event_tickets.gatya_event_data.items:
            for gset in item.sets:
                if gset.number == -1:
                    continue

                gset_opt = None

                if item.get_normal_flag():
                    gset_opt = event_tickets.gatya_option_n.get(gset.number)
                elif item.get_rare_flag():
                    gset_opt = event_tickets.gatya_option_r.get(gset.number)
                elif item.get_collab_flag():
                    gset_opt = event_tickets.gatya_option_e.get(gset.number)

                if gset_opt is None:
                    continue

                gatya_item = event_tickets.gatya_item_buy.get(gset_opt.ticket_item_id)
                if gatya_item is None:
                    continue

                category = gatya_item.category
                if category in [
                    core.GatyaItemCategory.EVENT_TICKETS.value,
                    core.GatyaItemCategory.LUCKY_TICKETS_1.value,
                    core.GatyaItemCategory.LUCKY_TICKETS_2.value,
                ]:
                    event_ticket_items.append((item, gset, gatya_item))

        event_names: list[str] = []
        values: list[int] = []

        for event_item, gset, gatya_item in event_ticket_items:
            start_y, start_m, start_d = split_yyyymmdd(event_item.filter.start_yyyymmdd)
            start_h, start_min = split_hhmm(event_item.filter.start_hhmm)
            end_y, end_m, end_d = split_yyyymmdd(event_item.filter.end_yyyymmdd)
            end_h, end_min = split_hhmm(event_item.filter.end_hhmm)
            time_str = f"{start_y}-{start_m:02}-{start_d:02} {start_h:02}:{start_min:02} -> {end_y}-{end_m:02}-{end_d:02} {end_h:02}:{end_min:02}"
            event_message = gset.message.replace("<br>", "\n")

            base_msg = f"{time_str}"
            item_name = event_tickets.gatya_item_names.get_name(gatya_item.id)
            if item_name is not None:
                base_msg += f" - {item_name}"

            if event_message:
                base_msg += f" - {event_message}"

            current_amount = event_tickets.get_ticket(gatya_item.id)

            if current_amount is not None:
                event_names.append(base_msg)
                values.append(current_amount)

        values = cli.dialog_creator.MultiEditor.from_reduced(
            "event_tickets",
            event_names,
            ints=values,
            max_values=core.core_data.max_value_manager.get("event_tickets"),
            group_name_localized=True,
        ).edit()

        for (event_item, gset, gatya_item), value in zip(event_ticket_items, values):
            event_tickets.edit_ticket(gatya_item.id, value)

    def get_ticket(self, item_id: int) -> int | None:
        item = self.gatya_item_buy.get(item_id)
        if item is None:
            return

        if item.category == core.GatyaItemCategory.EVENT_TICKETS.value:
            if item.index < len(self.save_file.event_capsules):
                return self.save_file.event_capsules[item.index]
        if item.category == core.GatyaItemCategory.LUCKY_TICKETS_1.value:
            if item.index < len(self.save_file.lucky_tickets):
                return self.save_file.lucky_tickets[item.index]
        if item.category == core.GatyaItemCategory.LUCKY_TICKETS_2.value:
            if item.index < len(self.save_file.event_capsules_2):
                return self.save_file.event_capsules_2[item.index]

        return None

    def edit_ticket(self, item_id: int, amount: int):
        item = self.gatya_item_buy.get(item_id)
        if item is None:
            return

        if item.category == core.GatyaItemCategory.EVENT_TICKETS.value:
            if item.index < len(self.save_file.event_capsules):
                self.save_file.event_capsules[item.index] = amount
        if item.category == core.GatyaItemCategory.LUCKY_TICKETS_1.value:
            if item.index < len(self.save_file.lucky_tickets):
                self.save_file.lucky_tickets[item.index] = amount
        if item.category == core.GatyaItemCategory.LUCKY_TICKETS_2.value:
            if item.index < len(self.save_file.event_capsules_2):
                self.save_file.event_capsules_2[item.index] = amount


================================================
FILE: src/bcsfe/cli/edits/fixes.py
================================================
from __future__ import annotations
from bcsfe import core
from bcsfe.cli import color
import datetime


class Fixes:
    @staticmethod
    def fix_gamatoto_crash(save_file: core.SaveFile):
        save_file.gamatoto.skin = 2

        color.ColoredText.localize("fix_gamatoto_crash_success")

    @staticmethod
    def fix_ototo_crash(save_file: core.SaveFile):
        save_file.ototo.cannons = core.game.gamoto.ototo.Cannons.init(
            save_file.game_version
        )
        color.ColoredText.localize("fix_ototo_crash_success")

    @staticmethod
    def fix_time_errors(save_file: core.SaveFile):
        save_file.date_3 = datetime.datetime.now()
        save_file.timestamp = datetime.datetime.now().timestamp()
        save_file.energy_penalty_timestamp = datetime.datetime.now().timestamp()

        color.ColoredText.localize("fix_time_errors_success")

        # 10 = 62 / hgt1 = ahead by too much
        # 11 = 63 / hgt0 = behind by too much
        # 12 = 61 / hgt2 = ahead by too much

        # date_3 - controls gacha errors (hgt2)
        # can't be ahead of the device time

        # timestamp - controls gacha errors (hgt1, hgt0)
        # can't be ahead by more than 10 minutes to device time
        # can't be behind by more than 1.5 days to device time

        # penalty_timestamp - controls energy / gamatoto errors
        # can't by ahead of device time
        # can't be ahead by more than 1 day to device time
        # can't be behind by more than 1 day to device time


================================================
FILE: src/bcsfe/cli/edits/map.py
================================================
from __future__ import annotations
from bcsfe import core
from bcsfe.cli import color, dialog_creator
from typing import Union

ChaptersType = Union[
    "core.EventChapters",
    "core.GauntletChapters",
    "core.LegendQuestChapters",
    "core.ZeroLegendsChapters",
    "core.Chapters",
]


def get_total_maps(chapters: ChaptersType) -> int:
    if isinstance(chapters, core.EventChapters):
        return chapters.get_lengths()[1]
    return len(chapters.chapters)


def unclear_stage(
    chapters: ChaptersType,
    map: int,
    star: int,
    stage: int,
    type: int | None = None,
) -> bool:
    if isinstance(chapters, core.EventChapters):
        if type is None:
            raise ValueError("Type must be specified for EventChapters!")
        return chapters.unclear_stage(type, map, star, stage)
    else:
        return chapters.unclear_stage(map, star, stage)


def clear_stage(
    chapters: ChaptersType,
    map: int,
    star: int,
    stage: int,
    clear_amount: int = 1,
    overwrite_clear_progress: bool = False,
    type: int | None = None,
    ensure_cleared_only: bool = False,
) -> bool:
    if isinstance(chapters, core.EventChapters):
        if type is None:
            raise ValueError("Type must be specified for EventChapters!")

        return chapters.clear_stage(
            type, map, star, stage, clear_amount, overwrite_clear_progress
        )
    else:
        return chapters.clear_stage(
            map,
            star,
            stage,
            clear_amount,
            overwrite_clear_progress,
            ensure_cleared_only=ensure_cleared_only,
        )


def unclear_rest(
    chapters: ChaptersType,
    stages: list[int],
    stars: int,
    id: int,
    type: int | None = None,
):
    if isinstance(chapters, core.EventChapters):
        if type is None:
            raise ValueError("Type must be specified for EventChapters!")
        chapters.unclear_rest(stages, stars, id, type)
    else:
        chapters.unclear_rest(stages, stars, id)


def get_total_stars(
    map_option: core.MapOption,
    base_index: int,
    chapters: ChaptersType,
    id: int,
    type: int | None = None,
) -> int:

    max_stars = get_max_stars(chapters, id, type)

    map_option_stars = map_option.get_map(base_index + id)
    if map_option_stars is not None:
        return min(max_stars, map_option_stars.crown_count)
    return max_stars


def get_max_max_stars(
    map_option: core.MapOption,
    base_index: int,
    ids: list[int],
    chapters: ChaptersType,
    type: int | None = None,
) -> int:
    m = 0
    for id in ids:
        val = get_total_stars(map_option, base_index, chapters, id, type)
        if val > m:
            m = val

    return m


def get_max_stars(
    chapters: ChaptersType,
    id: int,
    type: int | None = None,
) -> int:

    if isinstance(chapters, core.EventChapters):
        if type is None:
            raise ValueError("Type must be specified for EventChapters!")
        max_stars = chapters.get_total_stars(type, id)
    else:
        max_stars = chapters.get_total_stars(id)

    return max_stars


def get_total_stages(
    chapters: ChaptersType, id: int, star: int, type: int | None = None
):
    if isinstance(chapters, core.EventChapters):
        if type is None:
            raise ValueError("Type must be specified for EventChapters!")
        total_stars = chapters.get_total_stages(type, id, star)
    else:
        total_stars = chapters.get_total_stages(id, star)

    return total_stars


def select_maps(
    save_file: core.SaveFile,
    chapters: ChaptersType,
    letter_code: str,
    base_index: int,
    no_r_prefix: bool = False,
) -> list[int] | None:
    map_names = core.MapNames(
        save_file, letter_code, no_r_prefix=no_r_prefix, base_index=base_index
    )
    names: dict[int, str | None] = {}
    for id, name in map_names.map_names.items():
        if id >= get_total_maps(chapters):
            continue
        names[id] = name

    return core.EventChapters.select_map_names(names)


def select_maps_stars(
    save_file: core.SaveFile,
    map_option: core.MapOption,
    chapters: ChaptersType,
    letter_code: str,
    base_index: int,
    type: int | None = None,
    no_r_prefix: bool = False,
) -> list[tuple[int, int]] | None:
    map_names = core.MapNames(
        save_file, letter_code, no_r_prefix=no_r_prefix, base_index=base_index
    )
    names: dict[int, str | None] = {}
    for id, name in map_names.map_names.items():
        if id >= get_total_maps(chapters):
            continue

        for star in range(get_total_stars(map_option, base_index, chapters, id, type)):
            names[id * 10 + star] = core.localize(
                "map_name_star", name=name, star=star + 1
            )

    ids = core.EventChapters.select_map_names(names)
    if ids is None:
        return None

    new_ids: list[tuple[int, int]] = []

    for id in ids:
        map_id = id // 10
        star_index = id % 10

        new_ids.append((map_id, star_index))

    return new_ids


def edit_chapters2_clear_count(
    save_file: core.SaveFile,
    chapters: ChaptersType,
    letter_code: str,
    base_index: int,
    type: int | None = None,
    no_r_prefix: bool = False,
):

    map_names = core.MapNames(
        save_file, letter_code, no_r_prefix=no_r_prefix, base_index=base_index
    )

    map_option = core.MapOption.from_save(save_file)
    if map_option is None:
        return None

    map_choices = select_maps_stars(
        save_file, map_option, chapters, letter_code, base_index, type, no_r_prefix
    )
    if map_choices is None:
        return None

    clear_all = edit_all_or_handle_ind(len(map_choices))
    if clear_all is None:
        return None

    if clear_all == 0:
        clear_count = core.EventChapters.ask_clear_amount()
        if clear_count is None:
            return None

        for local_map_id, star in map_choices:
            total_stages = get_total_stages(chapters, local_map_id, star, type)
            for stage in range(total_stages):
                clear_stage(chapters, local_map_id, star, stage, clear_count, type=type)
    else:
        for local_map_id, star in map_choices:
            print()
            core.EventChapters.print_current_chapter(
                core.localize(
                    "map_name_star",
                    star=star,
                    name=map_names.map_names.get(local_map_id),
                ),
                local_map_id,
            )
            clear_whole = dialog_creator.ChoiceInput.from_reduced(
                ["edit_whole_chapter", "edit_specific_stages"], dialog="edit_chapter_q"
            ).single_choice()
            if clear_whole is None:
                return None

            clear_whole -= 1

            if clear_whole == 0:
                clear_count = core.EventChapters.ask_clear_amount()
                if clear_count is None:
                    return None

                for stage in range(
                    get_total_stages(chapters, local_map_id, star, type)
                ):
                    clear_stage(
                        chapters, local_map_id, star, stage, clear_count, type=type
                    )
            else:
                stage_ids = core.EventChapters.ask_stages(map_names, local_map_id)

                if stage_ids is None:
                    return None

                all_selected_stages = dialog_creator.ChoiceInput.from_reduced(
                    ["each_stage_individually", "stage_all_at_once"],
                    dialog="set_clear_count_stage_q",
                ).single_choice()
                if all_selected_stages is None:
                    return None

                all_selected_stages -= 1

                stage_names = core.EventChapters.get_stage_names(
                    map_names, local_map_id
                )
                if stage_names is None:
                    stage_names = []
                if all_selected_stages == 0:
                    for stage in stage_ids:
                        print()
                        if stage < len(stage_names):
                            stage_name = stage_names[stage]
                        else:
                            stage_name = None
                        core.EventChapters.print_current_stage(stage_name, stage)
                        clear_count = core.EventChapters.ask_clear_amount()
                        if clear_count is None:
                            return None
                        clear_stage(
                            chapters, local_map_id, star, stage, clear_count, type=type
                        )
                else:
                    clear_count = core.EventChapters.ask_clear_amount()
                    if clear_count is None:
                        return None
                    for stage in stage_ids:
                        clear_stage(
                            chapters, local_map_id, star, stage, clear_count, type=type
                        )


def clear_all_or_handle_ind(map_choices_len: int) -> int | None:
    if map_choices_len <= 1:
        clear_all = 1
    else:
        clear_all = dialog_creator.ChoiceInput.from_reduced(
            ["clear_all", "handle_individually"], dialog="clear_chapters_q"
        ).single_choice()
        if clear_all is None:
            return None

        clear_all -= 1

    return clear_all


def unclear_all_or_handle_ind(map_choices_len: int) -> int | None:
    if map_choices_len <= 1:
        clear_all = 1
    else:
        clear_all = dialog_creator.ChoiceInput.from_reduced(
            ["unclear_all", "handle_individually"], dialog="unclear_chapters_q"
        ).single_choice()
        if clear_all is None:
            return None

        clear_all -= 1

    return clear_all


def edit_all_or_handle_ind(map_choices_len: int) -> int | None:
    if map_choices_len <= 1:
        clear_all = 1
    else:
        clear_all = dialog_creator.ChoiceInput.from_reduced(
            ["edit_map_all", "handle_individually"], dialog="edit_chapters_q_all"
        ).single_choice()
        if clear_all is None:
            return None

        clear_all -= 1

    return clear_all


def edit_chapters2_progress(
    save_file: core.SaveFile,
    chapters: ChaptersType,
    letter_code: str,
    base_index: int,
    type: int | None = None,
    no_r_prefix: bool = False,
    allow_unclear: bool = False,
):
    map_names = core.MapNames(
        save_file, letter_code, no_r_prefix=no_r_prefix, base_index=base_index
    )

    map_choices = select_maps(save_file, chapters, letter_code, base_index, no_r_prefix)
    if map_choices is None:
        return None

    clear_all = clear_all_or_handle_ind(len(map_choices))
    if clear_all is None:
        return None

    map_option = core.MapOption.from_save(save_file)
    if map_option is None:
        return None

    if clear_all == 0:
        max_stars = get_max_max_stars(
            map_option, base_index, map_choices, chapters, type
        )
        if allow_unclear:
            stars = core.EventChapters.ask_stars_unclear(max_stars, "max_stars")
        else:
            stars = core.EventChapters.ask_stars(max_stars, "max_stars")
        if stars is None:
            return None
        for local_map_id in map_choices:
            unclear_rest(
                chapters,
                [0],
                max(0, stars - 1),
                local_map_id,
                type,
            )
            for star in range(stars):
                total_stages = get_total_stages(chapters, local_map_id, star, type)
                for stage in range(total_stages):
                    clear_stage(
                        chapters,
                        local_map_id,
                        star,
                        stage,
                        type=type,
                        ensure_cleared_only=True,
                    )

        return map_choices

    for local_map_id in map_choices:
        name = map_names.map_names.get(local_map_id)
        core.EventChapters.print_current_chapter(name, local_map_id)
        clear_whole = dialog_creator.ChoiceInput.from_reduced(
            ["clear_whole_chapter", "clear_to_specific_stage"], dialog="clear_whole_q"
        ).single_choice()
        if clear_whole is None:
            return None

        clear_whole -= 1

        if clear_whole == 0:
            max_stars = get_total_stars(
                map_option, base_index, chapters, local_map_id, type
            )
            if allow_unclear:
                stars = core.EventChapters.ask_stars_unclear(max_stars)
            else:
                stars = core.EventChapters.ask_stars(max_stars)
            if stars is None:
                return None

            unclear_rest(
                chapters,
                [0],
                max(stars - 1, 0),
                local_map_id,
                type,
            )

            for star in range(stars):
                total_stages = get_total_stages(chapters, local_map_id, star, type)
                for stage in range(total_stages):
                    clear_stage(
                        chapters,
                        local_map_id,
                        star,
                        stage,
                        type=type,
                        ensure_cleared_only=True,
                    )

        else:
            stage_names = map_names.stage_names.get(local_map_id)
            stage_names = [
                stage_name
                for stage_name in stage_names or []
                if stage_name and stage_name != "@"
            ]
            stage_id = core.EventChapters.ask_stages_stage_names_one(stage_names)
            if stage_id is None:
                return None

            max_stars = get_total_stars(
                map_option, base_index, chapters, local_map_id, type
            )

            if allow_unclear:
                stars = core.EventChapters.ask_stars_unclear(max_stars)
            else:
                stars = core.EventChapters.ask_stars(max_stars)
            if stars is None:
                return None

            unclear_rest(
                chapters, list(range(stage_id)), max(stars - 1, 0), local_map_id, type
            )

            for star in range(stars - 1):
                total_stages = get_total_stages(chapters, local_map_id, star, type)
                for stage in range(total_stages):
                    clear_stage(
                        chapters,
                        local_map_id,
                        star,
                        stage,
                        type=type,
                        ensure_cleared_only=True,
                    )

            for stage in range(stage_id + 1):
                clear_stage(
                    chapters,
                    local_map_id,
                    stars - 1,
                    stage,
                    type=type,
                    ensure_cleared_only=True,
                )


def edit_chapters(
    save_file: core.SaveFile,
    chapters: ChaptersType,
    letter_code: str,
    base_index: int,
    type: int | None = None,
    no_r_prefix: bool = False,
) -> dict[int, bool] | None:
    while True:
        choice = dialog_creator.ChoiceInput.from_reduced(
            [
                "edit_progress_clear",
                "edit_progress_unclear",
                "edit_clear_counts",
                "finish",
            ],
            dialog="edit_chapters_q",
        ).single_choice()
        if choice is None:
            return None
        choice -= 1

        if choice == 0:
            edit_chapters2_progress(
                save_file, chapters, letter_code, base_index, type, no_r_prefix
            )
        elif choice == 1:
            edit_chapters2_progress(
                save_file,
                chapters,
                letter_code,
                base_index,
                type,
                no_r_prefix,
                allow_unclear=True,
            )
        elif choice == 2:
            edit_chapters2_clear_count(
                save_file, chapters, letter_code, base_index, type, no_r_prefix
            )
        else:
            break
        color.ColoredText.localize("map_chapters_edited")
    color.ColoredText.localize("map_chapters_edited")

    return None


================================================
FILE: src/bcsfe/cli/edits/max_all.py
================================================
from __future__ import annotations

from collections.abc import Callable
from bcsfe import core


def max_catfood(save_file: core.SaveFile):
    orig = save_file.catfood
    save_file.catfood = core.core_data.max_value_manager.get(core.MaxValueType.CATFOOD)
    core.BackupMetaData(save_file).add_managed_item(
        core.ManagedItem.from_change(
            save_file.catfood - orig, core.ManagedItemType.CATFOOD
        )
    )


def max_rare_tickets(save_file: core.SaveFile):
    orig = save_file.rare_tickets
    save_file.rare_tickets = core.core_data.max_value_manager.get(
        core.MaxValueType.RARE_TICKETS
    )
    core.BackupMetaData(save_file).add_managed_item(
        core.ManagedItem.from_change(
            save_file.rare_tickets - orig, core.ManagedItemType.RARE_TICKET
        )
    )


def max_plat_tickets(save_file: core.SaveFile):
    orig = save_file.platinum_tickets
    save_file.platinum_tickets = core.core_data.max_value_manager.get(
        core.MaxValueType.PLATINUM_TICKETS
    )
    core.BackupMetaData(save_file).add_managed_item(
        core.ManagedItem.from_change(
            save_file.platinum_tickets - orig, core.ManagedItemType.PLATINUM_TICKET
        )
    )


def max_plat_shards(save_file: core.SaveFile):
    save_file.platinum_shards = 10 * core.core_data.max_value_manager.get(
        core.MaxValueType.PLATINUM_TICKETS
    )


def max_legend_tickets(save_file: core.SaveFile):
    orig = save_file.legend_tickets
    save_file.legend_tickets = core.core_data.max_value_manager.get(
        core.MaxValueType.LEGEND_TICKETS
    )
    core.BackupMetaData(save_file).add_managed_item(
        core.ManagedItem.from_change(
            save_file.legend_tickets - orig, core.ManagedItemType.LEGEND_TICKET
        )
    )


def max_xp(save_file: core.SaveFile):
    save_file.xp = core.core_data.max_value_manager.get(core.MaxValueType.XP)


def max_np(save_file: core.SaveFile):
    save_file.np = core.core_data.max_value_manager.get(core.MaxValueType.NP)


def max_100_million_ticket(save_file: core.SaveFile):
    save_file.hundred_million_ticket = core.core_data.max_value_manager.get(
        core.MaxValueType.HUNDRED_MILLION_TICKETS
    )


def max_leadership(save_file: core.SaveFile):
    save_file.leadership = core.core_data.max_value_manager.get(
        core.MaxValueType.LEADERSHIP
    )


def max_battle_items(save_file: core.SaveFile):
    for item in save_file.battle_items.items:
        item.amount = core.core_data.max_value_manager.get(
            core.MaxValueType.BATTLE_ITEMS
        )


def max_catseyes(save_file: core.SaveFile):
    for id in range(len(save_file.catseyes)):
        save_file.catseyes[id] = core.core_data.max_value_manager.get(
            core.MaxValueType.CATSEYES
        )


def max_treasure_chests(save_file: core.SaveFile):
    for id in range(len(save_file.treasure_chests)):
        save_file.treasure_chests[id] = core.core_data.max_value_manager.get(
            core.MaxValueType.TREASURE_CHESTS
        )


def max_catamins(save_file: core.SaveFile):
    for id in range(len(save_file.catseyes)):
        save_file.catamins[id] = core.core_data.max_value_manager.get(
            core.MaxValueType.CATAMINS
        )


def max_labyrinth_medals(save_file: core.SaveFile):
    for id in range(len(save_file.labyrinth_medals)):
        save_file.labyrinth_medals[id] = core.core_data.max_value_manager.get(
            core.MaxValueType.LABYRINTH_MEDALS
        )


# def max_catfruit(save_file: core.SaveFile):
#     for id in range(len(save_file.catfruit)):
#         save_file.catfruit[id] = core.core_data.max_value_manager.get_new(
#             core.MaxValueType.CATFRUIT
#         )


def max_normal_tickets(save_file: core.SaveFile):
    save_file.normal_tickets = core.core_data.max_value_manager.get(
        core.MaxValueType.NORMAL_TICKETS
    )


def max_all(save_file: core.SaveFile):
    maxes = core.core_data.max_value_manager
    features: dict[str, Callable[[core.SaveFile], None]] = {
        "catfood": max_catfood,
        "xp": max_xp,
        "normal_tickets": max_normal_tickets,
        "rare_tickets": max_rare_tickets,
        "platinum_tickets": max_plat_tickets,
        "legend_tickets": max_legend_tickets,
        "platinum_shards": max_plat_shards,
        "np": max_np,
        "leadership": max_leadership,
        "battle_items": max_battle_items,
        "catseyes": max_catseyes,
        "catamins": max_catamins,
        "labyrinth_medals": max_labyrinth_medals,
        "100_million_ticket": max_100_million_ticket,
        "treasure_chests": max_treasure_chests,
    }
    # TODO: finish


================================================
FILE: src/bcsfe/cli/edits/rare_ticket_trade.py
================================================
from __future__ import annotations
from bcsfe import core

from bcsfe.cli import color, dialog_creator


class RareTicketTrade:
    @staticmethod
    def rare_ticket_trade(save_file: core.SaveFile):
        current_amount = save_file.rare_tickets
        max_amount = max(
            core.core_data.max_value_manager.get("rare_tickets")
            - current_amount,
            0,
        )
        if max_amount == 0:
            color.ColoredText.localize("rare_ticket_trade_maxed")
            return
        to_add = dialog_creator.IntInput(max_amount, 0).get_input_locale_while(
            "rare_ticket_trade_enter",
            {"max": max_amount, "current": current_amount},
        )
        if to_add is None:
            return

        space = False
        for storage_item in save_file.cats.storage_items:
            if storage_item.item_type == 0 or (
                storage_item.item_id == 1 and storage_item.item_type == 2
            ):
                storage_item.item_id = 1
                storage_item.item_type = 2
                space = True
                break

        if not space:
            color.ColoredText.localize("rare_ticket_trade_storage_full")
            return

        amount = to_add * 5
        save_file.gatya.trade_progress = amount

        color.ColoredText.localize(
            "rare_ticket_successfully_traded", rare_ticket_count=to_add
        )


================================================
FILE: src/bcsfe/cli/edits/storage.py
================================================
from __future__ import annotations
from bcsfe import core
from bcsfe.cli import color, dialog_creator
from bcsfe.cli.edits import cat_editor


def display_storage(save_file: core.SaveFile, storage: list[core.StorageItem]):
    color.ColoredText.localize("current_storage_items")
    index = 0
    for item in storage:
        if item.item_type == 0:
            continue

        index += 1
        color.ColoredText(f"{index}. ", end="")
        display_item(item, save_file)

    if index == 0:
        color.ColoredText.localize("storage_is_empty")

    available_slots = len(storage) - index

    color.ColoredText.localize("available_storage", slots=available_slots)


def display_item(item: core.StorageItem, save_file: core.SaveFile):
    color.ColoredText(get_item_str(item, save_file))


def get_item_str(item: core.StorageItem, save_file: core.SaveFile) -> str:
    if item.item_type == 1:
        cat_id = item.item_id
        names = core.Cat.get_names(cat_id, save_file)

        if not names:
            names = [str(cat_id)]

        return core.localize("cat", name=names[0], id=cat_id)
    elif item.item_type == 2:
        skill_id = item.item_id

        skill_names = (
            core.core_data.get_gatya_item_buy(save_file).get_names_by_category(
                core.GatyaItemCategory.SPECIAL_SKILLS
            )
            or []
        )

        if skill_id >= len(skill_names) or skill_id < 0:
            name = str(skill_id)
        else:
            name = skill_names[skill_id][1]

        return core.localize("special_skill", name=name, id=skill_id)
    elif item.item_type == 3:
        item_id = item.item_id

        name = core.core_data.get_gatya_item_names(save_file).get_name(item_id)
        if name is None:
            name = str(item_id)

        return core.localize("item", name=name, id=item_id)
    else:
        return core.localize(
            "unrecognised_storage_item", item_type=item.item_type, id=item.item_id
        )


def clear_storage(storage: list[core.StorageItem]):
    for item in storage:
        item.item_id = 0
        item.item_type = 0


def add_item(storage: list[core.StorageItem], item: core.StorageItem) -> bool:
    for citem in storage:
        if citem.item_type == 0:
            citem.item_type = item.item_type
            citem.item_id = item.item_id
            return True
    return False


def get_storage_space(storage: list[core.StorageItem]) -> int:
    space = 0

    for item in storage:
        if item.item_type == 0:
            space += 1
    return space


def edit_storage(save_file: core.SaveFile):
    display_storage(save_file, save_file.cats.storage_items)
    exit = False
    while not exit:
        exit = edit_loop(save_file)

    color.ColoredText.localize("storage_success")


def edit_loop(save_file: core.SaveFile) -> bool:
    storage = save_file.cats.storage_items

    options = [
        "display_storage",
        "clear_storage",
        "add_cats",
        "add_special_skills",
        "remove_items",
        "finish",
    ]

    choice = dialog_creator.ChoiceInput.from_reduced(
        options, dialog="select_option"
    ).single_choice()
    if choice is None:
        return False

    choice -= 1

    if choice == 0:
        display_storage(save_file, storage)
    if choice == 1:
        clear_storage(storage)
    elif choice == 2:
        editor, cats = cat_editor.CatEditor.from_save_file(save_file)
        if editor is None:
            return False

        space = get_storage_space(storage)
        if len(cats) > len(storage):
            color.ColoredText.localize(
                "too_many_cats_selected", max=len(storage), current=len(cats)
            )
            return False

        needs = len(cats) - space
        if needs > 0:
            color.ColoredText.localize("need_x_more_space", needs=needs)
            return False

        color.ColoredText.localize("added_cats")
        for cat in cats:
            item = core.StorageItem.from_cat(cat.id)
            add_item(storage, item)
            display_item(item, save_file)
    elif choice == 3:

        skill_names: list[str] = list(
            map(
                lambda sk: sk[1] or str(sk[0].id),
                core.core_data.get_gatya_item_buy(save_file).get_names_by_category(
                    core.GatyaItemCategory.SPECIAL_SKILLS
                )
                or [],
            )
        )

        options, _ = dialog_creator.ChoiceInput.from_reduced(
            skill_names, localize_options=False, dialog="select_special_skills"
        ).multiple_choice(False)

        if options is None:
            return False

        space = get_storage_space(storage)
        if len(options) > len(storage):
            color.ColoredText.localize(
                "too_many_skills_selected", max=len(storage), current=len(options)
            )
            return False

        needs = len(options) - space
        if needs > 0:
            color.ColoredText.localize("need_x_more_space", needs=needs)
            return False

        color.ColoredText.localize("added_special_skills")
        for choice in options:
            item = core.StorageItem.from_special_skill(choice)
            add_item(storage, item)
            display_item(item, save_file)

    elif choice == 4:
        options2: list[str] = []
        for item in storage:
            if item.item_type == 0:
                continue
            options2.append(get_item_str(item, save_file))

        choices, _ = dialog_creator.ChoiceInput.from_reduced(
            options2, localize_options=False
        ).multiple_choice(False)
        if choices is None:
            return False

        color.ColoredText.localize("removed_items")
        index = 0
        for item in storage:
            if item.item_type == 0:
                continue

            if index in choices:
                display_item(item, save_file)
                item.item_type = 0
                item.item_id = 0

            index += 1

    elif choice == 5:
        return True

    return False


================================================
FILE: src/bcsfe/cli/feature_handler.py
================================================
from __future__ import annotations
from typing import Any, Callable
from bcsfe import core
from bcsfe.cli import dialog_creator, color, edits, save_management, main


class FeatureHandler:
    def __init__(self, save_file: core.SaveFile):
        self.save_file = save_file

    def get_features(self) -> dict[str, Any]:
        cat_features = {"cats": edits.cat_editor.CatEditor.edit_cats}
        if core.core_data.config.get_bool(core.ConfigKey.SEPARATE_CAT_EDIT_OPTIONS):
            cat_features = {
                "unlock_remove_cats": edits.cat_editor.CatEditor.unlock_remove_cats_run,
                "upgrade_cats": edits.cat_editor.CatEditor.upgrade_cats_run,
                "true_form_remove_form_cats": edits.cat_editor.CatEditor.true_form_remove_form_cats_run,
                "force_true_form_cats": edits.cat_editor.CatEditor.force_true_form_cats_run,
                "fourth_form_remove_form_cats": edits.cat_editor.CatEditor.fourth_form_remove_form_cats_run,
                "force_fourth_form_cats": edits.cat_editor.CatEditor.force_fourth_form_cats_run,
                "upgrade_talents_remove_talents_cats": edits.cat_editor.CatEditor.upgrade_talents_remove_talents_cats_run,
                "unlock_remove_cat_guide": edits.cat_editor.CatEditor.unlock_cat_guide_remove_guide_run,
            }

        cat_features["special_skills"] = (
            edits.basic_items.BasicItems.edit_special_skills
        )

        cat_features["cat_storage"] = edits.storage.edit_storage

        features: dict[str, Any] = {
            "save_management": {
                "save_save": save_management.SaveManagement.save_save,
                "save_upload": save_management.SaveManagement.save_upload,
                "save_save_file": save_management.SaveManagement.save_save_dialog,
                core.localize(
                    "save_save_documents", path=core.SaveFile.get_save_path()
                ): save_management.SaveManagement.save_save_data_dir,
                "waydroid_push": save_management.SaveManagement.waydroid_push,
                "waydroid_push_rerun": save_management.SaveManagement.waydroid_push_rerun,
                "adb_push": save_management.SaveManagement.adb_push,
                "adb_push_rerun": save_management.SaveManagement.adb_push_rerun,
                "root_push": save_management.SaveManagement.root_push,
                "root_push_rerun": save_management.SaveManagement.root_push_rerun,
                "export_save": save_management.SaveManagement.export_save,
                "load_save": save_management.SaveManagement.load_save,
                # "init_save": save_management.SaveManagement.init_save,
                "convert_region": save_management.SaveManagement.convert_save_cc,
                "convert_version": save_management.SaveManagement.convert_save_gv,
            },
            "items": {
                "catfood": edits.basic_items.BasicItems.edit_catfood,
                "xp": edits.basic_items.BasicItems.edit_xp,
                "normal_tickets": edits.basic_items.BasicItems.edit_normal_tickets,
                "rare_tickets": edits.basic_items.BasicItems.edit_rare_tickets,
                "rare_ticket_trade_feature_name": edits.rare_ticket_trade.RareTicketTrade.rare_ticket_trade,
                "platinum_tickets": edits.basic_items.BasicItems.edit_platinum_tickets,
                "legend_tickets": edits.basic_items.BasicItems.edit_legend_tickets,
               
Download .txt
gitextract_nh3e0iid/

├── CHANGELOG.md
├── LICENSE
├── LOCALIZATION.md
├── MANIFEST.in
├── README.md
├── pyproject.toml
├── requirements.txt
├── setup.py
├── src/
│   └── bcsfe/
│       ├── __init__.py
│       ├── __main__.py
│       ├── cli/
│       │   ├── __init__.py
│       │   ├── color.py
│       │   ├── dialog_creator.py
│       │   ├── edits/
│       │   │   ├── __init__.py
│       │   │   ├── aku_realm.py
│       │   │   ├── basic_items.py
│       │   │   ├── cat_editor.py
│       │   │   ├── clear_tutorial.py
│       │   │   ├── enemy_editor.py
│       │   │   ├── event_tickets.py
│       │   │   ├── fixes.py
│       │   │   ├── map.py
│       │   │   ├── max_all.py
│       │   │   ├── rare_ticket_trade.py
│       │   │   └── storage.py
│       │   ├── feature_handler.py
│       │   ├── file_dialog.py
│       │   ├── main.py
│       │   ├── recent_saves.py
│       │   ├── save_management.py
│       │   └── server_cli.py
│       ├── core/
│       │   ├── __init__.py
│       │   ├── country_code.py
│       │   ├── crypto.py
│       │   ├── game/
│       │   │   ├── __init__.py
│       │   │   ├── battle/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── battle_items.py
│       │   │   │   ├── cleared_slots.py
│       │   │   │   ├── enemy.py
│       │   │   │   └── slots.py
│       │   │   ├── catbase/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── beacon_base.py
│       │   │   │   ├── cat.py
│       │   │   │   ├── drop_chara.py
│       │   │   │   ├── gambling.py
│       │   │   │   ├── gatya.py
│       │   │   │   ├── gatya_item.py
│       │   │   │   ├── item_pack.py
│       │   │   │   ├── login_bonuses.py
│       │   │   │   ├── matatabi.py
│       │   │   │   ├── medals.py
│       │   │   │   ├── mission.py
│       │   │   │   ├── my_sale.py
│       │   │   │   ├── nyanko_club.py
│       │   │   │   ├── officer_pass.py
│       │   │   │   ├── playtime.py
│       │   │   │   ├── powerup.py
│       │   │   │   ├── scheme_items.py
│       │   │   │   ├── special_skill.py
│       │   │   │   ├── stamp.py
│       │   │   │   ├── talent_orbs.py
│       │   │   │   ├── unlock_popups.py
│       │   │   │   ├── upgrade.py
│       │   │   │   └── user_rank_rewards.py
│       │   │   ├── gamoto/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── base_materials.py
│       │   │   │   ├── cat_shrine.py
│       │   │   │   ├── catamins.py
│       │   │   │   ├── gamatoto.py
│       │   │   │   └── ototo.py
│       │   │   ├── localizable.py
│       │   │   └── map/
│       │   │       ├── __init__.py
│       │   │       ├── aku.py
│       │   │       ├── challenge.py
│       │   │       ├── chapters.py
│       │   │       ├── dojo.py
│       │   │       ├── enigma.py
│       │   │       ├── event.py
│       │   │       ├── ex_stage.py
│       │   │       ├── gauntlets.py
│       │   │       ├── item_reward_stage.py
│       │   │       ├── legend_quest.py
│       │   │       ├── map_names.py
│       │   │       ├── map_option.py
│       │   │       ├── map_reset.py
│       │   │       ├── outbreaks.py
│       │   │       ├── story.py
│       │   │       ├── timed_score.py
│       │   │       ├── tower.py
│       │   │       ├── uncanny.py
│       │   │       └── zero_legends.py
│       │   ├── game_version.py
│       │   ├── io/
│       │   │   ├── __init__.py
│       │   │   ├── adb_handler.py
│       │   │   ├── bc_csv.py
│       │   │   ├── command.py
│       │   │   ├── config.py
│       │   │   ├── data.py
│       │   │   ├── git_handler.py
│       │   │   ├── json_file.py
│       │   │   ├── path.py
│       │   │   ├── root_handler.py
│       │   │   ├── save.py
│       │   │   ├── thread_helper.py
│       │   │   ├── waydroid.py
│       │   │   └── yaml.py
│       │   ├── locale_handler.py
│       │   ├── log.py
│       │   ├── max_value_helper.py
│       │   ├── server/
│       │   │   ├── __init__.py
│       │   │   ├── client_info.py
│       │   │   ├── event_data.py
│       │   │   ├── game_data_getter.py
│       │   │   ├── headers.py
│       │   │   ├── managed_item.py
│       │   │   ├── request.py
│       │   │   ├── server_handler.py
│       │   │   └── updater.py
│       │   └── theme_handler.py
│       ├── files/
│       │   ├── locales/
│       │   │   ├── en/
│       │   │   │   ├── core/
│       │   │   │   │   ├── config.properties
│       │   │   │   │   ├── files.properties
│       │   │   │   │   ├── input.properties
│       │   │   │   │   ├── locale.properties
│       │   │   │   │   ├── main.properties
│       │   │   │   │   ├── save.properties
│       │   │   │   │   ├── server.properties
│       │   │   │   │   ├── theme.properties
│       │   │   │   │   └── updater.properties
│       │   │   │   └── edits/
│       │   │   │       ├── bannable_items.properties
│       │   │   │       ├── cats.properties
│       │   │   │       ├── enemy.properties
│       │   │   │       ├── fixes.properties
│       │   │   │       ├── gambling.properties
│       │   │   │       ├── gamototo.properties
│       │   │   │       ├── gatya.properties
│       │   │   │       ├── gold_pass.properties
│       │   │   │       ├── items.properties
│       │   │   │       ├── map.properties
│       │   │   │       ├── medals.properties
│       │   │   │       ├── missions.properties
│       │   │   │       ├── playtime.properties
│       │   │   │       ├── scheme_items.properties
│       │   │   │       ├── special_skills.properties
│       │   │   │       ├── talent_orbs.properties
│       │   │   │       ├── treasures.properties
│       │   │   │       └── user_rank.properties
│       │   │   ├── tw/
│       │   │   │   ├── core/
│       │   │   │   │   ├── config.properties
│       │   │   │   │   ├── files.properties
│       │   │   │   │   ├── input.properties
│       │   │   │   │   ├── locale.properties
│       │   │   │   │   ├── main.properties
│       │   │   │   │   ├── save.properties
│       │   │   │   │   ├── server.properties
│       │   │   │   │   ├── theme.properties
│       │   │   │   │   └── updater.properties
│       │   │   │   ├── edits/
│       │   │   │   │   ├── bannable_items.properties
│       │   │   │   │   ├── cats.properties
│       │   │   │   │   ├── enemy.properties
│       │   │   │   │   ├── fixes.properties
│       │   │   │   │   ├── gambling.properties
│       │   │   │   │   ├── gamototo.properties
│       │   │   │   │   ├── gatya.properties
│       │   │   │   │   ├── gold_pass.properties
│       │   │   │   │   ├── items.properties
│       │   │   │   │   ├── map.properties
│       │   │   │   │   ├── medals.properties
│       │   │   │   │   ├── missions.properties
│       │   │   │   │   ├── playtime.properties
│       │   │   │   │   ├── scheme_items.properties
│       │   │   │   │   ├── special_skills.properties
│       │   │   │   │   ├── talent_orbs.properties
│       │   │   │   │   ├── treasures.properties
│       │   │   │   │   └── user_rank.properties
│       │   │   │   └── metadata.json
│       │   │   └── vi/
│       │   │       ├── core/
│       │   │       │   ├── config.properties
│       │   │       │   ├── files.properties
│       │   │       │   ├── input.properties
│       │   │       │   ├── locale.properties
│       │   │       │   ├── main.properties
│       │   │       │   ├── save.properties
│       │   │       │   ├── server.properties
│       │   │       │   ├── theme.properties
│       │   │       │   └── updater.properties
│       │   │       ├── edits/
│       │   │       │   ├── bannable_items.properties
│       │   │       │   ├── cats.properties
│       │   │       │   ├── enemy.properties
│       │   │       │   ├── fixes.properties
│       │   │       │   ├── gambling.properties
│       │   │       │   ├── gamototo.properties
│       │   │       │   ├── gatya.properties
│       │   │       │   ├── gold_pass.properties
│       │   │       │   ├── items.properties
│       │   │       │   ├── map.properties
│       │   │       │   ├── medals.properties
│       │   │       │   ├── missions.properties
│       │   │       │   ├── playtime.properties
│       │   │       │   ├── scheme_items.properties
│       │   │       │   ├── special_skills.properties
│       │   │       │   ├── talent_orbs.properties
│       │   │       │   ├── treasures.properties
│       │   │       │   └── user_rank.properties
│       │   │       └── metadata.json
│       │   ├── max_values.json
│       │   └── themes/
│       │       ├── default.json
│       │       └── discord.json
│       └── py.typed
└── tests/
    ├── __init__.py
    └── test_parse.py
Download .txt
Showing preview only (222K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2892 symbols across 103 files)

FILE: src/bcsfe/__init__.py
  function run (line 9) | def run():

FILE: src/bcsfe/__main__.py
  function main (line 12) | def main():

FILE: src/bcsfe/cli/color.py
  class ColorHex (line 8) | class ColorHex(NamedConstant):
    method from_name (line 45) | def from_name(name: str) -> str:
  class ColorHelper (line 51) | class ColorHelper:
    method __init__ (line 52) | def __init__(self):
    method get_color (line 55) | def get_color(self, color_name: str) -> str:
  class ColoredText (line 92) | class ColoredText:
    method __init__ (line 93) | def __init__(self, string: str, end: str = "\n") -> None:
    method display (line 100) | def display(self, string: str) -> None:
    method localize (line 116) | def localize(string: str, escape: bool = True, **kwargs: Any) -> Color...
    method parse (line 121) | def parse(self, txt: str) -> list[tuple[str, str]]:
  class ColoredInput (line 169) | class ColoredInput:
    method __init__ (line 170) | def __init__(self, end: str = "") -> None:
    method get (line 173) | def get(self, display_string: str) -> str:
    method localize (line 177) | def localize(self, string: str, escape: bool = True, **kwargs: Any) ->...

FILE: src/bcsfe/cli/dialog_creator.py
  class RangeInput (line 7) | class RangeInput:
    method __init__ (line 8) | def __init__(self, max: int | None = None, min: int = 0):
    method clamp_value (line 12) | def clamp_value(self, value: int) -> int:
    method get_input_locale (line 17) | def get_input_locale(
    method parse (line 28) | def parse(self, user_input: str) -> list[int] | None:
  class IntInput (line 68) | class IntInput:
    method __init__ (line 69) | def __init__(
    method get_max_value (line 85) | def get_max_value(
    method clamp_value (line 102) | def clamp_value(self, value: int) -> int:
    method get_input (line 105) | def get_input(
    method get_input_locale_while (line 123) | def get_input_locale_while(
    method get_input_locale (line 133) | def get_input_locale(
    method get_basic_input_locale (line 149) | def get_basic_input_locale(self, localization_key: str, perameters: di...
  class ListOutput (line 159) | class ListOutput:
    method __init__ (line 160) | def __init__(
    method get_output (line 178) | def get_output(self, dialog: str | None, strings: list[str]) -> str:
    method display (line 194) | def display(self, dialog: str | None, strings: list[str]) -> None:
    method display_locale (line 198) | def display_locale(self, remove_alias: bool = False) -> None:
    method display_non_locale (line 214) | def display_non_locale(self) -> None:
  class ChoiceInput (line 218) | class ChoiceInput:
    method __init__ (line 219) | def __init__(
    method from_reduced (line 244) | def from_reduced(
    method get_input (line 274) | def get_input(self) -> tuple[int | None, str]:
    method get_input_while (line 289) | def get_input_while(self) -> int | None:
    method get_max_value (line 304) | def get_max_value(self) -> int:
    method get_min_value (line 307) | def get_min_value(self) -> int:
    method get_input_locale (line 310) | def get_input_locale(self, localized: bool = True) -> tuple[list[int] ...
    method get_input_locale_while (line 381) | def get_input_locale_while(self) -> list[int] | None:
    method multiple_choice (line 398) | def multiple_choice(
    method single_choice (line 407) | def single_choice(self) -> int | None:
    method get (line 410) | def get(self) -> tuple[int | None | list[int], bool]:
  class MultiEditor (line 416) | class MultiEditor:
    method __init__ (line 417) | def __init__(
    method from_reduced (line 462) | def from_reduced(
    method edit (line 493) | def edit(self) -> list[int]:
    method edit_all (line 509) | def edit_all(self, choices: list[int]) -> list[int]:
    method edit_one (line 551) | def edit_one(self, choices: list[int]) -> list[int]:
  class SingleEditor (line 585) | class SingleEditor:
    method __init__ (line 586) | def __init__(
    method edit (line 608) | def edit(self, escape_text: bool = True) -> int:
  class StringInput (line 641) | class StringInput:
    method __init__ (line 642) | def __init__(self, default: str = ""):
    method get_input_locale_while (line 645) | def get_input_locale_while(
    method get_input_locale (line 658) | def get_input_locale(
  class StringEditor (line 675) | class StringEditor:
    method __init__ (line 676) | def __init__(self, item: str, value: str, item_localized: bool = False):
    method edit (line 682) | def edit(self) -> str:
  class YesNoInput (line 697) | class YesNoInput:
    method __init__ (line 698) | def __init__(self, default: bool = False):
    method get_input_once (line 701) | def get_input_once(
  class DialogBuilder (line 719) | class DialogBuilder:
    method __init__ (line 720) | def __init__(self, dialog_structure: dict[Any, Any]):

FILE: src/bcsfe/cli/edits/aku_realm.py
  function unlock_aku_realm (line 6) | def unlock_aku_realm(save_file: core.SaveFile):

FILE: src/bcsfe/cli/edits/basic_items.py
  class BasicItems (line 8) | class BasicItems:
    method get_name (line 10) | def get_name(name: str | None, key: str) -> str:
    method reset_golden_cat_cpus (line 16) | def reset_golden_cat_cpus(save_file: core.SaveFile):
    method edit_catfood (line 22) | def edit_catfood(save_file: core.SaveFile):
    method edit_xp (line 40) | def edit_xp(save_file: core.SaveFile):
    method edit_normal_tickets (line 49) | def edit_normal_tickets(save_file: core.SaveFile):
    method edit_100_million_ticket (line 58) | def edit_100_million_ticket(save_file: core.SaveFile):
    method get_bannable_feature_options (line 68) | def get_bannable_feature_options(feature_name: str, safe_feature_name:...
    method edit_rare_tickets (line 96) | def edit_rare_tickets(save_file: core.SaveFile):
    method edit_platinum_tickets (line 119) | def edit_platinum_tickets(save_file: core.SaveFile):
    method edit_legend_tickets (line 142) | def edit_legend_tickets(save_file: core.SaveFile):
    method edit_platinum_shards (line 161) | def edit_platinum_shards(save_file: core.SaveFile):
    method edit_np (line 177) | def edit_np(save_file: core.SaveFile):
    method edit_leadership (line 186) | def edit_leadership(save_file: core.SaveFile):
    method edit_battle_items (line 195) | def edit_battle_items(save_file: core.SaveFile):
    method edit_battle_items_endless (line 199) | def edit_battle_items_endless(save_file: core.SaveFile):
    method edit_catamins (line 203) | def edit_catamins(save_file: core.SaveFile):
    method edit_catseyes (line 226) | def edit_catseyes(save_file: core.SaveFile):
    method edit_treasure_chests (line 250) | def edit_treasure_chests(save_file: core.SaveFile):
    method edit_catfruit (line 276) | def edit_catfruit(save_file: core.SaveFile):
    method set_restart_pack (line 318) | def set_restart_pack(save_file: core.SaveFile):
    method edit_inquiry_code (line 324) | def edit_inquiry_code(save_file: core.SaveFile):
    method edit_password_refresh_token (line 337) | def edit_password_refresh_token(save_file: core.SaveFile):
    method edit_scheme_items (line 350) | def edit_scheme_items(save_file: core.SaveFile):
    method edit_engineers (line 354) | def edit_engineers(save_file: core.SaveFile):
    method edit_base_materials (line 358) | def edit_base_materials(save_file: core.SaveFile):
    method edit_rare_gatya_seed (line 362) | def edit_rare_gatya_seed(save_file: core.SaveFile):
    method edit_normal_gatya_seed (line 366) | def edit_normal_gatya_seed(save_file: core.SaveFile):
    method edit_event_gatya_seed (line 370) | def edit_event_gatya_seed(save_file: core.SaveFile):
    method edit_unlocked_slots (line 374) | def edit_unlocked_slots(save_file: core.SaveFile):
    method edit_labyrinth_medals (line 378) | def edit_labyrinth_medals(save_file: core.SaveFile):
    method edit_special_skills (line 402) | def edit_special_skills(save_file: core.SaveFile):
    method unlock_equip_menu (line 406) | def unlock_equip_menu(save_file: core.SaveFile):
    method allow_filibuster_stage_reclearing (line 411) | def allow_filibuster_stage_reclearing(save_file: core.SaveFile):

FILE: src/bcsfe/cli/edits/cat_editor.py
  class SelectMode (line 9) | class SelectMode(enum.Enum):
  class CatEditor (line 15) | class CatEditor:
    method __init__ (line 16) | def __init__(self, save_file: core.SaveFile):
    method get_current_cats (line 19) | def get_current_cats(self):
    method get_non_unlocked_cats (line 22) | def get_non_unlocked_cats(self):
    method get_non_gacha_cats (line 25) | def get_non_gacha_cats(self):
    method filter_cats (line 28) | def filter_cats(self, cats: list[core.Cat]) -> list[core.Cat]:
    method get_cats_rarity (line 32) | def get_cats_rarity(self, rarity: int) -> list[core.Cat]:
    method get_cats_name (line 35) | def get_cats_name(self, name: str) -> list[core.Cat]:
    method get_cats_obtainable (line 38) | def get_cats_obtainable(self) -> list[core.Cat] | None:
    method get_cats_unobtainable (line 41) | def get_cats_unobtainable(self) -> list[core.Cat] | None:
    method get_cats_gatya_banner (line 44) | def get_cats_gatya_banner(self, gatya_id: int) -> list[core.Cat] | None:
    method print_selected_cats (line 47) | def print_selected_cats(self, current_cats: list[core.Cat]):
    method select (line 57) | def select(
    method select_id (line 116) | def select_id(self) -> list[core.Cat] | None:
    method select_cats_game_version (line 124) | def select_cats_game_version(self) -> list[core.Cat] | None:
    method select_rarity (line 196) | def select_rarity(self) -> list[core.Cat] | None:
    method select_name (line 209) | def select_name(self) -> list[core.Cat] | None:
    method select_obtainable (line 240) | def select_obtainable(self) -> list[core.Cat] | None:
    method select_gatya_banner_name (line 243) | def select_gatya_banner_name(self) -> list[int] | None:
    method select_gatya_banner (line 297) | def select_gatya_banner(self) -> list[core.Cat] | None:
    method unlock_cats (line 324) | def unlock_cats(self, cats: list[core.Cat]):
    method remove_cats (line 330) | def remove_cats(self, cats: list[core.Cat]):
    method get_save_cats (line 337) | def get_save_cats(self, cats: list[core.Cat]):
    method true_form_cats (line 346) | def true_form_cats(self, cats: list[core.Cat], force: bool = False):
    method fourth_form_cats (line 356) | def fourth_form_cats(self, cats: list[core.Cat], force: bool = False):
    method remove_true_form_cats (line 366) | def remove_true_form_cats(self, cats: list[core.Cat]):
    method remove_fourth_form_cats (line 372) | def remove_fourth_form_cats(self, cats: list[core.Cat]):
    method upgrade_cats (line 378) | def upgrade_cats(self, cats: list[core.Cat]):
    method remove_talents_cats (line 444) | def remove_talents_cats(self, cats: list[core.Cat]):
    method unlock_cat_guide (line 452) | def unlock_cat_guide(self, cats: list[core.Cat]):
    method remove_cat_guide (line 459) | def remove_cat_guide(self, cats: list[core.Cat]):
    method upgrade_talents_cats (line 464) | def upgrade_talents_cats(self, cats: list[core.Cat]):
    method edit_cats (line 551) | def edit_cats(save_file: core.SaveFile):
    method unlock_remove_cats_run (line 561) | def unlock_remove_cats_run(
    method true_form_remove_form_cats_run (line 589) | def true_form_remove_form_cats_run(
    method fourth_form_remove_form_cats_run (line 612) | def fourth_form_remove_form_cats_run(
    method force_true_form_cats_run (line 635) | def force_true_form_cats_run(save_file: core.SaveFile):
    method force_fourth_form_cats_run (line 643) | def force_fourth_form_cats_run(save_file: core.SaveFile):
    method upgrade_cats_run (line 651) | def upgrade_cats_run(save_file: core.SaveFile):
    method upgrade_talents_remove_talents_cats_run (line 659) | def upgrade_talents_remove_talents_cats_run(
    method unlock_cat_guide_remove_guide_run (line 685) | def unlock_cat_guide_remove_guide_run(
    method from_save_file (line 711) | def from_save_file(
    method run_edit_cats (line 732) | def run_edit_cats(
    method set_rank_up_sale (line 788) | def set_rank_up_sale(save_file: core.SaveFile):

FILE: src/bcsfe/cli/edits/clear_tutorial.py
  function clear_tutorial (line 6) | def clear_tutorial(

FILE: src/bcsfe/cli/edits/enemy_editor.py
  class EnemyEditor (line 9) | class EnemyEditor:
    method __init__ (line 10) | def __init__(self, save_file: core.SaveFile) -> None:
    method unlock_enemy_guide (line 13) | def unlock_enemy_guide(self, enemies: list[core.Enemy]):
    method remove_enemy_guide (line 19) | def remove_enemy_guide(self, enemies: list[core.Enemy]):
    method print_selected_enemies (line 25) | def print_selected_enemies(self, enemies: list[core.Enemy]):
    method select (line 38) | def select(self, current_enemies: list[core.Enemy] | None):
    method get_all_enemies (line 85) | def get_all_enemies(self) -> list[core.Enemy]:
    method get_all_valid_enemies (line 91) | def get_all_valid_enemies(self) -> list[core.Enemy] | None:
    method get_all_invalid_enemies (line 98) | def get_all_invalid_enemies(self) -> list[core.Enemy] | None:
    method select_id (line 107) | def select_id(self) -> list[core.Enemy] | None:
    method get_enemies_by_id (line 116) | def get_enemies_by_id(self, ids: list[int]) -> list[core.Enemy]:
    method select_name (line 123) | def select_name(self) -> list[core.Enemy] | None:
    method get_enemies_by_name (line 150) | def get_enemies_by_name(self, name: str) -> list[core.Enemy]:
    method from_save_file (line 161) | def from_save_file(
    method edit_enemy_guide (line 171) | def edit_enemy_guide(

FILE: src/bcsfe/cli/edits/event_tickets.py
  class EventTickets (line 7) | class EventTickets:
    method __init__ (line 8) | def __init__(self, save_file: core.SaveFile):
    method edit (line 34) | def edit(save_file: core.SaveFile):
    method get_ticket (line 118) | def get_ticket(self, item_id: int) -> int | None:
    method edit_ticket (line 135) | def edit_ticket(self, item_id: int, amount: int):

FILE: src/bcsfe/cli/edits/fixes.py
  class Fixes (line 7) | class Fixes:
    method fix_gamatoto_crash (line 9) | def fix_gamatoto_crash(save_file: core.SaveFile):
    method fix_ototo_crash (line 15) | def fix_ototo_crash(save_file: core.SaveFile):
    method fix_time_errors (line 22) | def fix_time_errors(save_file: core.SaveFile):

FILE: src/bcsfe/cli/edits/map.py
  function get_total_maps (line 15) | def get_total_maps(chapters: ChaptersType) -> int:
  function unclear_stage (line 21) | def unclear_stage(
  function clear_stage (line 36) | def clear_stage(
  function unclear_rest (line 64) | def unclear_rest(
  function get_total_stars (line 79) | def get_total_stars(
  function get_max_max_stars (line 95) | def get_max_max_stars(
  function get_max_stars (line 111) | def get_max_stars(
  function get_total_stages (line 127) | def get_total_stages(
  function select_maps (line 140) | def select_maps(
  function select_maps_stars (line 159) | def select_maps_stars(
  function edit_chapters2_clear_count (line 196) | def edit_chapters2_clear_count(
  function clear_all_or_handle_ind (line 306) | def clear_all_or_handle_ind(map_choices_len: int) -> int | None:
  function unclear_all_or_handle_ind (line 321) | def unclear_all_or_handle_ind(map_choices_len: int) -> int | None:
  function edit_all_or_handle_ind (line 336) | def edit_all_or_handle_ind(map_choices_len: int) -> int | None:
  function edit_chapters2_progress (line 351) | def edit_chapters2_progress(
  function edit_chapters (line 499) | def edit_chapters(

FILE: src/bcsfe/cli/edits/max_all.py
  function max_catfood (line 7) | def max_catfood(save_file: core.SaveFile):
  function max_rare_tickets (line 17) | def max_rare_tickets(save_file: core.SaveFile):
  function max_plat_tickets (line 29) | def max_plat_tickets(save_file: core.SaveFile):
  function max_plat_shards (line 41) | def max_plat_shards(save_file: core.SaveFile):
  function max_legend_tickets (line 47) | def max_legend_tickets(save_file: core.SaveFile):
  function max_xp (line 59) | def max_xp(save_file: core.SaveFile):
  function max_np (line 63) | def max_np(save_file: core.SaveFile):
  function max_100_million_ticket (line 67) | def max_100_million_ticket(save_file: core.SaveFile):
  function max_leadership (line 73) | def max_leadership(save_file: core.SaveFile):
  function max_battle_items (line 79) | def max_battle_items(save_file: core.SaveFile):
  function max_catseyes (line 86) | def max_catseyes(save_file: core.SaveFile):
  function max_treasure_chests (line 93) | def max_treasure_chests(save_file: core.SaveFile):
  function max_catamins (line 100) | def max_catamins(save_file: core.SaveFile):
  function max_labyrinth_medals (line 107) | def max_labyrinth_medals(save_file: core.SaveFile):
  function max_normal_tickets (line 121) | def max_normal_tickets(save_file: core.SaveFile):
  function max_all (line 127) | def max_all(save_file: core.SaveFile):

FILE: src/bcsfe/cli/edits/rare_ticket_trade.py
  class RareTicketTrade (line 7) | class RareTicketTrade:
    method rare_ticket_trade (line 9) | def rare_ticket_trade(save_file: core.SaveFile):

FILE: src/bcsfe/cli/edits/storage.py
  function display_storage (line 7) | def display_storage(save_file: core.SaveFile, storage: list[core.Storage...
  function display_item (line 26) | def display_item(item: core.StorageItem, save_file: core.SaveFile):
  function get_item_str (line 30) | def get_item_str(item: core.StorageItem, save_file: core.SaveFile) -> str:
  function clear_storage (line 69) | def clear_storage(storage: list[core.StorageItem]):
  function add_item (line 75) | def add_item(storage: list[core.StorageItem], item: core.StorageItem) ->...
  function get_storage_space (line 84) | def get_storage_space(storage: list[core.StorageItem]) -> int:
  function edit_storage (line 93) | def edit_storage(save_file: core.SaveFile):
  function edit_loop (line 102) | def edit_loop(save_file: core.SaveFile) -> bool:

FILE: src/bcsfe/cli/feature_handler.py
  class FeatureHandler (line 7) | class FeatureHandler:
    method __init__ (line 8) | def __init__(self, save_file: core.SaveFile):
    method get_features (line 11) | def get_features(self) -> dict[str, Any]:
    method get_feature (line 147) | def get_feature(self, feature_path: list[str]):
    method search_features (line 155) | def search_features(
    method display_features (line 195) | def display_features(self, features: list[list[str]]):
    method select_features (line 204) | def select_features(
    method select_features_run (line 245) | def select_features_run(self):
    method do_save_actions (line 287) | def do_save_actions(self):

FILE: src/bcsfe/cli/file_dialog.py
  class FileDialog (line 6) | class FileDialog:
    method load_tk (line 7) | def load_tk(self):
    method __init__ (line 18) | def __init__(self):
    method select_files_in_dir (line 31) | def select_files_in_dir(
    method use_tk (line 78) | def use_tk(self) -> bool:
    method get_file (line 85) | def get_file(
    method save_file (line 118) | def save_file(

FILE: src/bcsfe/cli/main.py
  class Main (line 18) | class Main:
    method __init__ (line 21) | def __init__(self):
    method wipe_temp_save (line 27) | def wipe_temp_save(self):
    method main (line 31) | def main(self, input_path: str | None = None):
    method version_check (line 43) | def version_check(self, v1: str, v2: str) -> bool:
    method check_update (line 63) | def check_update(self):
    method print_start_text (line 133) | def print_start_text(self):
    method load_save_options (line 187) | def load_save_options(self, input_path: str | None = None):
    method feature_handler (line 206) | def feature_handler(self):
    method save_save_dialog (line 214) | def save_save_dialog(save_file: core.SaveFile) -> core.Path | None:
    method save_json_dialog (line 236) | def save_json_dialog(json_data: dict[str, Any]) -> core.Path | None:
    method load_save_file (line 258) | def load_save_file() -> core.Path | None:
    method load_save_data_json (line 276) | def load_save_data_json() -> tuple[core.Path, core.CountryCode] | None:
    method exit_editor (line 311) | def exit_editor(
    method leave (line 359) | def leave() -> NoReturn:

FILE: src/bcsfe/cli/recent_saves.py
  class RecentSave (line 10) | class RecentSave:
    method __init__ (line 11) | def __init__(
    method from_dict (line 28) | def from_dict(data: dict[str, Any]) -> RecentSave | None:
    method to_dict (line 54) | def to_dict(self) -> dict[str, Any]:
  class RecentSaves (line 65) | class RecentSaves:
    method __init__ (line 66) | def __init__(self, saves: list[RecentSave]):
    method from_json (line 70) | def from_json(data: list[dict[str, Any]]) -> RecentSaves:
    method to_json (line 80) | def to_json(self) -> list[dict[str, Any]]:
    method from_path (line 84) | def from_path(path: core.Path) -> RecentSaves | None:
    method to_path (line 89) | def to_path(self, path: core.Path):
    method read_default (line 98) | def read_default() -> RecentSaves:
    method get_path (line 105) | def get_path() -> core.Path:
    method save_default (line 108) | def save_default(self):
    method select (line 112) | def select(self) -> RecentSave | None:

FILE: src/bcsfe/cli/save_management.py
  class SaveManagement (line 11) | class SaveManagement:
    method __init__ (line 12) | def __init__(self):
    method save_save (line 16) | def save_save(save_file: core.SaveFile, check_strict: bool = True):
    method save_save_dialog (line 39) | def save_save_dialog(save_file: core.SaveFile):
    method save_save_data_dir (line 55) | def save_save_data_dir(save_file: core.SaveFile):
    method save_upload (line 67) | def save_upload(save_file: core.SaveFile):
    method unban_account (line 91) | def unban_account(save_file: core.SaveFile):
    method create_new_account (line 105) | def create_new_account(save_file: core.SaveFile):
    method waydroid_push (line 119) | def waydroid_push(save_file: core.SaveFile) -> core.WayDroidHandler | ...
    method waydroid_push_rerun (line 155) | def waydroid_push_rerun(save_file: core.SaveFile) -> core.AdbHandler |...
    method adb_push (line 168) | def adb_push(save_file: core.SaveFile) -> core.AdbHandler | None:
    method root_push (line 206) | def root_push(save_file: core.SaveFile) -> core.RootHandler | None:
    method adb_push_rerun (line 243) | def adb_push_rerun(save_file: core.SaveFile):
    method root_push_rerun (line 261) | def root_push_rerun(save_file: core.SaveFile):
    method export_save (line 279) | def export_save(save_file: core.SaveFile):
    method init_save (line 294) | def init_save(save_file: core.SaveFile):
    method upload_items (line 307) | def upload_items(save_file: core.SaveFile, check_strict: bool = True):
    method upload_items_checker (line 328) | def upload_items_checker(save_file: core.SaveFile, check_strict: bool ...
    method select_save (line 340) | def select_save(
    method load_save_file_path (line 537) | def load_save_file_path(
    method select_package_name (line 591) | def select_package_name(package_names: list[str]) -> str | None:
    method load_save (line 603) | def load_save(save_file: core.SaveFile):
    method convert_save_cc (line 619) | def convert_save_cc(save_file: core.SaveFile):
    method convert_save_gv (line 631) | def convert_save_gv(save_file: core.SaveFile):

FILE: src/bcsfe/cli/server_cli.py
  class ServerCLI (line 6) | class ServerCLI:
    method __init__ (line 7) | def __init__(self):
    method download_save (line 10) | def download_save(

FILE: src/bcsfe/core/__init__.py
  class CoreData (line 178) | class CoreData:
    method init_data (line 179) | def init_data(self):
    method get_game_data_getter (line 202) | def get_game_data_getter(
    method get_gatya_item_names (line 220) | def get_gatya_item_names(self, save: SaveFile) -> GatyaItemNames:
    method get_gatya_item_buy (line 225) | def get_gatya_item_buy(self, save: SaveFile) -> GatyaItemBuy:
    method get_chara_drop (line 230) | def get_chara_drop(self, save: SaveFile) -> CharaDrop:
    method get_gamatoto_levels (line 235) | def get_gamatoto_levels(self, save: SaveFile) -> GamatotoLevels:
    method get_gamatoto_members_name (line 240) | def get_gamatoto_members_name(self, save: SaveFile) -> GamatotoMembers...
    method get_localizable (line 245) | def get_localizable(self, save: SaveFile) -> Localizable:
    method get_ability_data (line 250) | def get_ability_data(self, save: SaveFile) -> AbilityData:
    method get_enemy_names (line 255) | def get_enemy_names(self, save: SaveFile) -> EnemyNames:
    method get_rank_gift_descriptions (line 260) | def get_rank_gift_descriptions(self, save: SaveFile) -> RankGiftDescri...
    method get_rank_gifts (line 265) | def get_rank_gifts(self, save: SaveFile) -> RankGifts:
    method get_treasure_text (line 270) | def get_treasure_text(self, save: SaveFile) -> TreasureText:
    method get_cat_shrine_levels (line 275) | def get_cat_shrine_levels(self, save: SaveFile) -> CatShrineLevels:
    method get_medal_names (line 280) | def get_medal_names(self, save: SaveFile) -> MedalNames:
    method get_mission_names (line 285) | def get_mission_names(self, save: SaveFile) -> MissionNames:
    method get_mission_conditions (line 290) | def get_mission_conditions(self, save: SaveFile) -> MissionConditions:
    method get_lang (line 295) | def get_lang(self, save: SaveFile) -> str:
  function set_config_path (line 306) | def set_config_path(path: Path):
  function set_game_data_path (line 311) | def set_game_data_path(path: Path):
  function set_log_path (line 316) | def set_log_path(path: Path):
  function set_transfer_backup_path (line 321) | def set_transfer_backup_path(path: Path):
  function get_transfer_backup_path (line 326) | def get_transfer_backup_path() -> Path | None:
  function get_game_data_path (line 330) | def get_game_data_path() -> Path | None:
  function update_external_content (line 334) | def update_external_content(_: Any = None):
  function print_no_internet (line 352) | def print_no_internet():
  function localize (line 359) | def localize(key: str, escape: bool = True, **kwargs: Any) -> str:

FILE: src/bcsfe/core/country_code.py
  class CountryCodeType (line 7) | class CountryCodeType(enum.Enum):
  class CountryCode (line 14) | class CountryCode:
    method __init__ (line 15) | def __init__(self, cc: str | CountryCodeType):
    method get_code (line 19) | def get_code(self) -> str:
    method get_client_info_code (line 22) | def get_client_info_code(self) -> str:
    method get_patching_code (line 28) | def get_patching_code(self) -> str:
    method from_patching_code (line 35) | def from_patching_code(code: str) -> CountryCode:
    method from_code (line 41) | def from_code(code: str) -> CountryCode:
    method get_all (line 45) | def get_all() -> list["CountryCode"]:
    method get_all_str (line 49) | def get_all_str() -> list[str]:
    method __str__ (line 53) | def __str__(self) -> str:
    method __repr__ (line 56) | def __repr__(self) -> str:
    method copy (line 59) | def copy(self) -> CountryCode:
    method select (line 63) | def select() -> CountryCode | None:
    method select_from_ccs (line 74) | def select_from_ccs(ccs: list[CountryCode]) -> CountryCode | None:
    method __eq__ (line 84) | def __eq__(self, o: object) -> bool:
    method get_cc_lang (line 93) | def get_cc_lang(self) -> core.CountryCode:
    method get_langs (line 100) | def get_langs() -> list[str]:
    method is_lang (line 103) | def is_lang(self) -> bool:

FILE: src/bcsfe/core/crypto.py
  class HashAlgorithm (line 9) | class HashAlgorithm(enum.Enum):
  class Hash (line 17) | class Hash:
    method __init__ (line 20) | def __init__(self, algorithm: HashAlgorithm):
    method get_hash (line 28) | def get_hash(
  class Random (line 59) | class Random:
    method get_bytes (line 63) | def get_bytes(length: int) -> bytes:
    method get_alpha_string (line 75) | def get_alpha_string(length: int) -> str:
    method get_hex_string (line 88) | def get_hex_string(length: int) -> str:
    method get_digits_string (line 101) | def get_digits_string(length: int) -> str:
  class Hmac (line 114) | class Hmac:
    method __init__ (line 115) | def __init__(self, algorithm: HashAlgorithm):
    method get_hmac (line 118) | def get_hmac(self, key: core.Data, data: core.Data) -> core.Data:
  class NyankoSignature (line 133) | class NyankoSignature:
    method __init__ (line 134) | def __init__(self, inquiry_code: str, data: str):
    method generate_signature (line 138) | def generate_signature(self) -> str:
    method generate_signature_v1 (line 151) | def generate_signature_v1(self) -> str:

FILE: src/bcsfe/core/game/battle/battle_items.py
  class EndlessItem (line 11) | class EndlessItem:
    method __init__ (line 12) | def __init__(
    method init (line 22) | def init() -> EndlessItem:
    method read (line 26) | def read(stream: core.Data) -> EndlessItem:
    method write (line 35) | def write(self, stream: core.Data):
    method serialize (line 42) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 52) | def deserialize(data: dict[str, Any]) -> EndlessItem:
    method get_endless_duration (line 61) | def get_endless_duration(self) -> datetime.timedelta | None:
    method get_endless_duration_formatted (line 74) | def get_endless_duration_formatted(self) -> str:
    method set_duration_mins (line 88) | def set_duration_mins(self, mins: float, amount: int):
  class BattleItem (line 96) | class BattleItem:
    method __init__ (line 97) | def __init__(self, amount: int):
    method init (line 104) | def init() -> BattleItem:
    method read_amount (line 108) | def read_amount(stream: core.Data) -> BattleItem:
    method write_amount (line 111) | def write_amount(self, stream: core.Data):
    method read_locked (line 114) | def read_locked(self, stream: core.Data):
    method write_locked (line 117) | def write_locked(self, stream: core.Data):
    method read_endless_items (line 120) | def read_endless_items(self, stream: core.Data):
    method write_endless_items (line 123) | def write_endless_items(self, stream: core.Data):
    method serialize (line 126) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 134) | def deserialize(data: dict[str, Any]) -> BattleItem:
    method __repr__ (line 140) | def __repr__(self):
    method __str__ (line 146) | def __str__(self):
  class BattleItems (line 150) | class BattleItems:
    method __init__ (line 151) | def __init__(self, items: list[BattleItem]):
    method init (line 156) | def init() -> BattleItems:
    method read_items (line 160) | def read_items(stream: core.Data) -> BattleItems:
    method write_items (line 165) | def write_items(self, stream: core.Data):
    method read_locked_items (line 169) | def read_locked_items(self, stream: core.Data):
    method write_locked_items (line 174) | def write_locked_items(self, stream: core.Data):
    method read_endless_items (line 179) | def read_endless_items(self, stream: core.Data):
    method write_endless_items (line 187) | def write_endless_items(self, stream: core.Data):
    method serialize (line 195) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 202) | def deserialize(data: dict[str, Any]) -> BattleItems:
    method __repr__ (line 209) | def __repr__(self):
    method __str__ (line 212) | def __str__(self):
    method get_names (line 215) | def get_names(self, save_file: core.SaveFile) -> list[str] | None:
    method edit (line 226) | def edit(self, save_file: core.SaveFile):
    method edit_endless_items (line 243) | def edit_endless_items(self, save_file: core.SaveFile):

FILE: src/bcsfe/core/game/battle/cleared_slots.py
  class CatSlot (line 6) | class CatSlot:
    method __init__ (line 7) | def __init__(self, cat_id: int, form: int):
    method init (line 12) | def init() -> CatSlot:
    method read (line 16) | def read(stream: core.Data) -> CatSlot:
    method write (line 21) | def write(self, stream: core.Data):
    method serialize (line 25) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 32) | def deserialize(data: dict[str, Any]) -> CatSlot:
    method __repr__ (line 35) | def __repr__(self):
    method __str__ (line 38) | def __str__(self):
  class LineupCat (line 42) | class LineupCat:
    method __init__ (line 43) | def __init__(
    method init (line 58) | def init() -> LineupCat:
    method read (line 63) | def read(stream: core.Data) -> LineupCat:
    method write (line 73) | def write(self, stream: core.Data):
    method serialize (line 81) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 91) | def deserialize(data: dict[str, Any]) -> LineupCat:
    method __repr__ (line 100) | def __repr__(self):
    method __str__ (line 103) | def __str__(self):
  class ClearedSlotsCat (line 107) | class ClearedSlotsCat:
    method __init__ (line 108) | def __init__(self, lineups: list[LineupCat]):
    method init (line 112) | def init() -> ClearedSlotsCat:
    method read (line 116) | def read(stream: core.Data) -> ClearedSlotsCat:
    method write (line 121) | def write(self, stream: core.Data):
    method serialize (line 126) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 130) | def deserialize(data: list[dict[str, Any]]) -> ClearedSlotsCat:
    method __repr__ (line 135) | def __repr__(self):
    method __str__ (line 138) | def __str__(self):
  class StageSlot (line 142) | class StageSlot:
    method __init__ (line 143) | def __init__(self, stage_id: int):
    method init (line 147) | def init() -> StageSlot:
    method read (line 151) | def read(stream: core.Data) -> StageSlot:
    method write (line 155) | def write(self, stream: core.Data):
    method serialize (line 158) | def serialize(self) -> int:
    method deserialize (line 162) | def deserialize(data: int) -> StageSlot:
    method __repr__ (line 165) | def __repr__(self):
    method __str__ (line 168) | def __str__(self):
  class StageLineups (line 172) | class StageLineups:
    method __init__ (line 173) | def __init__(self, index: int, slots: list[StageSlot]):
    method init (line 178) | def init() -> StageLineups:
    method read (line 182) | def read(stream: core.Data) -> StageLineups:
    method write (line 188) | def write(self, stream: core.Data):
    method serialize (line 194) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 201) | def deserialize(data: dict[str, Any]) -> StageLineups:
    method __repr__ (line 207) | def __repr__(self):
    method __str__ (line 210) | def __str__(self):
  class ClearedStageSlots (line 214) | class ClearedStageSlots:
    method __init__ (line 215) | def __init__(self, lineups: list[StageLineups]):
    method init (line 219) | def init() -> ClearedStageSlots:
    method read (line 223) | def read(stream: core.Data) -> ClearedStageSlots:
    method write (line 228) | def write(self, stream: core.Data):
    method serialize (line 233) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 239) | def deserialize(data: dict[str, Any]) -> ClearedStageSlots:
    method __repr__ (line 247) | def __repr__(self):
    method __str__ (line 250) | def __str__(self):
  class ClearedSlots (line 254) | class ClearedSlots:
    method __init__ (line 255) | def __init__(
    method init (line 266) | def init() -> ClearedSlots:
    method read (line 274) | def read(stream: core.Data) -> ClearedSlots:
    method write (line 281) | def write(self, stream: core.Data):
    method serialize (line 287) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 295) | def deserialize(data: dict[str, Any]) -> ClearedSlots:
    method __repr__ (line 302) | def __repr__(self):
    method __str__ (line 305) | def __str__(self):

FILE: src/bcsfe/core/game/battle/enemy.py
  class Enemy (line 5) | class Enemy:
    method __init__ (line 6) | def __init__(self, id: int):
    method unlock_enemy_guide (line 9) | def unlock_enemy_guide(self, save_file: core.SaveFile):
    method reset_enemy_guide (line 12) | def reset_enemy_guide(self, save_file: core.SaveFile):
    method get_name (line 15) | def get_name(self, save_file: core.SaveFile) -> str | None:
  class EnemyDictionaryItem (line 19) | class EnemyDictionaryItem:
    method __init__ (line 20) | def __init__(self, enemy_id: int, scale: int, first_seen: int | None):
  class EnemyDictionary (line 26) | class EnemyDictionary:
    method __init__ (line 27) | def __init__(self, save_file: core.SaveFile):
    method __get_dictionary (line 31) | def __get_dictionary(self) -> list[EnemyDictionaryItem] | None:
    method get_valid_enemies (line 50) | def get_valid_enemies(self) -> list[int] | None:
    method get_invalid_enemies (line 56) | def get_invalid_enemies(self, total_enemies: int) -> list[int] | None:
  class EnemyDescription (line 66) | class EnemyDescription:
    method __init__ (line 67) | def __init__(self, trait_str: str, description: list[str] | None):
  class EnemyDescriptions (line 72) | class EnemyDescriptions:
    method __init__ (line 73) | def __init__(self, save_file: core.SaveFile):
    method __get_descriptions (line 77) | def __get_descriptions(self) -> list[EnemyDescription] | None:
  class EnemyNames (line 100) | class EnemyNames:
    method __init__ (line 101) | def __init__(self, save_file: core.SaveFile):
    method get_names (line 105) | def get_names(self) -> list[str] | None:
    method get_name (line 121) | def get_name(self, id: int) -> str | None:

FILE: src/bcsfe/core/game/battle/slots.py
  class EquipSlot (line 7) | class EquipSlot:
    method __init__ (line 8) | def __init__(self, cat_id: int):
    method read (line 12) | def read(stream: core.Data) -> EquipSlot:
    method write (line 15) | def write(self, stream: core.Data):
    method serialize (line 18) | def serialize(self) -> int:
    method deserialize (line 22) | def deserialize(data: int) -> EquipSlot:
    method __repr__ (line 25) | def __repr__(self):
    method __str__ (line 28) | def __str__(self):
  class EquipSlots (line 32) | class EquipSlots:
    method __init__ (line 33) | def __init__(self, slots: list[EquipSlot]):
    method read (line 38) | def read(stream: core.Data) -> EquipSlots:
    method init (line 44) | def init() -> EquipSlots:
    method write (line 49) | def write(self, stream: core.Data):
    method read_name (line 53) | def read_name(self, stream: core.Data):
    method write_name (line 61) | def write_name(self, stream: core.Data):
    method serialize (line 64) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 71) | def deserialize(data: dict[str, Any]) -> EquipSlots:
    method __repr__ (line 78) | def __repr__(self):
    method __str__ (line 81) | def __str__(self):
  class LineUps (line 85) | class LineUps:
    method __init__ (line 86) | def __init__(self, slots: list[EquipSlots], total_slots: int = 15):
    method init (line 93) | def init(gv: core.GameVersion) -> LineUps:
    method read (line 102) | def read(stream: core.Data, gv: core.GameVersion) -> LineUps:
    method write (line 110) | def write(self, stream: core.Data, gv: core.GameVersion):
    method read_2 (line 123) | def read_2(self, stream: core.Data, gv: core.GameVersion):
    method write_2 (line 132) | def write_2(self, stream: core.Data, gv: core.GameVersion):
    method read_slot_names (line 143) | def read_slot_names(self, stream: core.Data, gv: core.GameVersion):
    method write_slot_names (line 158) | def write_slot_names(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 169) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 178) | def deserialize(data: dict[str, Any]) -> LineUps:
    method __repr__ (line 187) | def __repr__(self):
    method __str__ (line 190) | def __str__(self):
    method edit_unlocked_slots (line 193) | def edit_unlocked_slots(self):

FILE: src/bcsfe/core/game/catbase/beacon_base.py
  class BeaconEventListScene (line 6) | class BeaconEventListScene:
    method __init__ (line 7) | def __init__(
    method init (line 18) | def init() -> BeaconEventListScene:
    method read (line 22) | def read(stream: core.Data) -> BeaconEventListScene:
    method write (line 34) | def write(self, stream: core.Data):
    method serialize (line 48) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 56) | def deserialize(data: dict[str, Any]) -> BeaconEventListScene:
    method __repr__ (line 63) | def __repr__(self):
    method __str__ (line 66) | def __str__(self):

FILE: src/bcsfe/core/game/catbase/cat.py
  class SkillLevel (line 6) | class SkillLevel:
    method __init__ (line 7) | def __init__(
    method get_total_levels (line 15) | def get_total_levels(self) -> int:
    method from_row (line 19) | def from_row(row: core.Row):
  class SkillLevelData (line 25) | class SkillLevelData:
    method __init__ (line 26) | def __init__(self, levels: list[SkillLevel] | None):
    method from_game_data (line 30) | def from_game_data(save_file: core.SaveFile) -> SkillLevelData | None:
    method get_skill_level (line 41) | def get_skill_level(self, id: int) -> SkillLevel | None:
  class Skill (line 50) | class Skill:
    method __init__ (line 51) | def __init__(
  class CatSkill (line 84) | class CatSkill:
    method __init__ (line 85) | def __init__(
    method from_row (line 96) | def from_row(row: core.Row):
  class CatSkills (line 121) | class CatSkills:
    method __init__ (line 122) | def __init__(self, skills: dict[int, CatSkill]):
    method from_game_data (line 126) | def from_game_data(save_file: core.SaveFile) -> CatSkills | None:
    method get_cat_skill (line 138) | def get_cat_skill(self, cat_id: int) -> CatSkill | None:
  class SkillNames (line 142) | class SkillNames:
    method __init__ (line 143) | def __init__(self, names: dict[int, str]):
    method from_game_data (line 147) | def from_game_data(save_file: core.SaveFile) -> SkillNames | None:
    method get_skill_name (line 160) | def get_skill_name(self, skill_id: int) -> str | None:
  class TalentData (line 164) | class TalentData:
    method __init__ (line 165) | def __init__(
    method from_game_data (line 176) | def from_game_data(save_file: core.SaveFile) -> TalentData | None:
    method get_skill_name (line 185) | def get_skill_name(self, skill_id: int) -> str | None:
    method get_skill_level (line 188) | def get_skill_level(self, skill_id: int) -> SkillLevel | None:
    method get_cat_skill (line 191) | def get_cat_skill(self, cat_id: int) -> CatSkill | None:
    method get_skill_from_cat (line 194) | def get_skill_from_cat(self, cat_id: int, skill_id: int) -> Skill | None:
    method get_talent_from_cat_skill (line 203) | def get_talent_from_cat_skill(self, cat: core.Cat, skill_id: int) -> T...
    method get_cat_skill_name (line 212) | def get_cat_skill_name(self, cat_id: int, skill_id: int) -> str | None:
    method get_cat_skill_level (line 218) | def get_cat_skill_level(self, cat_id: int, skill_id: int) -> SkillLeve...
    method get_cat_talents (line 224) | def get_cat_talents(
  class Talent (line 253) | class Talent:
    method __init__ (line 254) | def __init__(self, id: int, level: int):
    method init (line 259) | def init() -> Talent:
    method reset (line 262) | def reset(self):
    method read (line 266) | def read(stream: core.Data):
    method write (line 269) | def write(self, stream: core.Data):
    method serialize (line 273) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 280) | def deserialize(data: dict[str, Any]) -> Talent:
    method __repr__ (line 286) | def __repr__(self):
    method __str__ (line 289) | def __str__(self):
  class NyankoPictureBookCatData (line 293) | class NyankoPictureBookCatData:
    method __init__ (line 294) | def __init__(
  class NyankoPictureBook (line 317) | class NyankoPictureBook:
    method __init__ (line 318) | def __init__(self, save_file: core.SaveFile):
    method get_cats (line 322) | def get_cats(self) -> list[NyankoPictureBookCatData] | None:
    method get_cat (line 344) | def get_cat(self, cat_id: int) -> NyankoPictureBookCatData | None:
    method get_obtainable_cats (line 352) | def get_obtainable_cats(self) -> list[NyankoPictureBookCatData] | None:
  class EvolveItem (line 358) | class EvolveItem:
    method __init__ (line 361) | def __init__(
    method __str__ (line 375) | def __str__(self) -> str:
    method __repr__ (line 383) | def __repr__(self) -> str:
  class EvolveItems (line 392) | class EvolveItems:
    method __init__ (line 395) | def __init__(self, evolve_items: list[EvolveItem]):
    method from_unit_buy_list (line 404) | def from_unit_buy_list(raw_data: core.Row, start_index: int) -> Evolve...
  class UnitBuyCatData (line 421) | class UnitBuyCatData:
    method __init__ (line 422) | def __init__(self, id: int, raw_data: core.Row):
    method assign (line 426) | def assign(self, raw_data: core.Row):
  class UnitBuy (line 464) | class UnitBuy:
    method __init__ (line 465) | def __init__(self, save_file: core.SaveFile):
    method read_unit_buy (line 469) | def read_unit_buy(self) -> list[UnitBuyCatData] | None:
    method get_unit_buy (line 480) | def get_unit_buy(self, id: int) -> UnitBuyCatData | None:
    method get_cat_rarity (line 488) | def get_cat_rarity(self, id: int) -> int:
  class UnitLimitCatData (line 495) | class UnitLimitCatData:
    method __init__ (line 496) | def __init__(self, cat_id: int, values: list[int]):
  class UnitLimit (line 501) | class UnitLimit:
    method __init__ (line 502) | def __init__(self, save_file: core.SaveFile):
    method read_unit_limit (line 506) | def read_unit_limit(self) -> list[UnitLimitCatData] | None:
    method get_unit_limit (line 517) | def get_unit_limit(self, id: int) -> UnitLimitCatData | None:
  class Cat (line 527) | class Cat:
    method __init__ (line 528) | def __init__(self, id: int, unlocked: int):
    method get_talent_from_id (line 543) | def get_talent_from_id(self, id: int) -> Talent | None:
    method unlock (line 549) | def unlock(self, save_file: core.SaveFile):
    method remove (line 555) | def remove(self, reset: bool = False, save_file: core.SaveFile | None ...
    method true_form (line 565) | def true_form(self, save_file: core.SaveFile, set_current_form: bool =...
    method set_form (line 568) | def set_form(
    method set_form_true (line 577) | def set_form_true(
    method remove_true_form (line 595) | def remove_true_form(self):
    method unlock_fourth_form (line 600) | def unlock_fourth_form(
    method remove_fourth_form (line 609) | def remove_fourth_form(self):
    method set_upgrade (line 613) | def set_upgrade(
    method upgrade_base (line 628) | def upgrade_base(self, save_file: core.SaveFile):
    method reset (line 633) | def reset(self):
    method init (line 646) | def init(id: int) -> Cat:
    method read_unlocked (line 650) | def read_unlocked(id: int, stream: core.Data):
    method write_unlocked (line 653) | def write_unlocked(self, stream: core.Data):
    method read_upgrade (line 656) | def read_upgrade(self, stream: core.Data):
    method write_upgrade (line 659) | def write_upgrade(self, stream: core.Data):
    method read_current_form (line 662) | def read_current_form(self, stream: core.Data):
    method write_current_form (line 665) | def write_current_form(self, stream: core.Data):
    method read_unlocked_forms (line 668) | def read_unlocked_forms(self, stream: core.Data):
    method write_unlocked_forms (line 671) | def write_unlocked_forms(self, stream: core.Data):
    method read_gatya_seen (line 674) | def read_gatya_seen(self, stream: core.Data):
    method write_gatya_seen (line 677) | def write_gatya_seen(self, stream: core.Data):
    method read_max_upgrade_level (line 680) | def read_max_upgrade_level(self, stream: core.Data):
    method write_max_upgrade_level (line 684) | def write_max_upgrade_level(self, stream: core.Data):
    method read_catguide_collected (line 687) | def read_catguide_collected(self, stream: core.Data):
    method write_catguide_collected (line 690) | def write_catguide_collected(self, stream: core.Data):
    method read_fourth_form (line 693) | def read_fourth_form(self, stream: core.Data):
    method write_fourth_form (line 696) | def write_fourth_form(self, stream: core.Data):
    method read_catseyes_used (line 699) | def read_catseyes_used(self, stream: core.Data):
    method write_catseyes_used (line 702) | def write_catseyes_used(self, stream: core.Data):
    method serialize (line 705) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 725) | def deserialize(data: dict[str, Any]) -> Cat:
    method __repr__ (line 742) | def __repr__(self) -> str:
    method __str__ (line 745) | def __str__(self) -> str:
    method read_talents (line 748) | def read_talents(self, stream: core.Data):
    method write_talents (line 753) | def write_talents(self, stream: core.Data):
    method get_names_cls (line 760) | def get_names_cls(self, save_file: core.SaveFile) -> list[str] | None:
    method get_names (line 766) | def get_names(
  class StorageItem (line 788) | class StorageItem:
    method __init__ (line 789) | def __init__(self, item_id: int):
    method from_cat (line 794) | def from_cat(cat_id: int) -> StorageItem:
    method from_special_skill (line 800) | def from_special_skill(special_skill_id: int) -> StorageItem:
    method init (line 806) | def init() -> StorageItem:
    method read_item_id (line 810) | def read_item_id(stream: core.Data):
    method write_item_id (line 813) | def write_item_id(self, stream: core.Data):
    method read_item_type (line 816) | def read_item_type(self, stream: core.Data):
    method write_item_type (line 819) | def write_item_type(self, stream: core.Data):
    method serialize (line 822) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 829) | def deserialize(data: dict[str, Any]) -> StorageItem:
    method __repr__ (line 834) | def __repr__(self) -> str:
    method __str__ (line 837) | def __str__(self) -> str:
  class Cats (line 841) | class Cats:
    method __init__ (line 842) | def __init__(self, cats: list[Cat], total_storage_items: int = 0):
    method get_all_cats (line 852) | def get_all_cats(self) -> list[Cat]:
    method init (line 856) | def init(gv: core.GameVersion) -> Cats:
    method get_gv_cats (line 871) | def get_gv_cats(gv: core.GameVersion) -> int | None:
    method get_unlocked_cats (line 888) | def get_unlocked_cats(self) -> list[Cat]:
    method get_non_unlocked_cats (line 891) | def get_non_unlocked_cats(self) -> list[Cat]:
    method get_non_gacha_cats (line 894) | def get_non_gacha_cats(self, save_file: core.SaveFile) -> list[Cat]:
    method read_unitbuy (line 907) | def read_unitbuy(self, save_file: core.SaveFile) -> UnitBuy:
    method read_unitlimit (line 912) | def read_unitlimit(self, save_file: core.SaveFile) -> UnitLimit:
    method read_nyanko_picture_book (line 917) | def read_nyanko_picture_book(self, save_file: core.SaveFile) -> Nyanko...
    method read_talent_data (line 922) | def read_talent_data(self, save_file: core.SaveFile) -> TalentData | N...
    method get_cats_rarity (line 927) | def get_cats_rarity(self, save_file: core.SaveFile, rarity: int) -> li...
    method get_cats_name (line 931) | def get_cats_name(
    method get_cats_obtainable (line 947) | def get_cats_obtainable(self, save_file: core.SaveFile) -> list[Cat] |...
    method get_cats_non_obtainable (line 959) | def get_cats_non_obtainable(self, save_file: core.SaveFile) -> list[Ca...
    method get_cats_gatya_banner (line 971) | def get_cats_gatya_banner(
    method true_form_cats (line 979) | def true_form_cats(
    method fourth_form_cats (line 998) | def fourth_form_cats(
    method get_cats_by_ids (line 1018) | def get_cats_by_ids(self, ids: list[int]) -> list[Cat]:
    method get_cat_by_id (line 1025) | def get_cat_by_id(self, id: int) -> Cat | None:
    method get_rarity_names (line 1032) | def get_rarity_names(save_file: core.SaveFile) -> list[str]:
    method read_unlocked (line 1045) | def read_unlocked(stream: core.Data, gv: core.GameVersion) -> Cats:
    method write_unlocked (line 1054) | def write_unlocked(self, stream: core.Data, gv: core.GameVersion):
    method read_upgrade (line 1061) | def read_upgrade(self, stream: core.Data, gv: core.GameVersion):
    method write_upgrade (line 1068) | def write_upgrade(self, stream: core.Data, gv: core.GameVersion):
    method read_current_form (line 1075) | def read_current_form(self, stream: core.Data, gv: core.GameVersion):
    method write_current_form (line 1082) | def write_current_form(self, stream: core.Data, gv: core.GameVersion):
    method read_unlocked_forms (line 1089) | def read_unlocked_forms(self, stream: core.Data, gv: core.GameVersion):
    method write_unlocked_forms (line 1096) | def write_unlocked_forms(self, stream: core.Data, gv: core.GameVersion):
    method read_gatya_seen (line 1103) | def read_gatya_seen(self, stream: core.Data, gv: core.GameVersion):
    method write_gatya_seen (line 1110) | def write_gatya_seen(self, stream: core.Data, gv: core.GameVersion):
    method read_max_upgrade_levels (line 1117) | def read_max_upgrade_levels(self, stream: core.Data, gv: core.GameVers...
    method write_max_upgrade_levels (line 1124) | def write_max_upgrade_levels(self, stream: core.Data, gv: core.GameVer...
    method read_storage (line 1131) | def read_storage(self, stream: core.Data, gv: core.GameVersion):
    method write_storage (line 1142) | def write_storage(self, stream: core.Data, gv: core.GameVersion):
    method read_catguide_collected (line 1150) | def read_catguide_collected(self, stream: core.Data):
    method write_catguide_collected (line 1155) | def write_catguide_collected(self, stream: core.Data):
    method read_fourth_forms (line 1160) | def read_fourth_forms(self, stream: core.Data):
    method read_catseyes_used (line 1165) | def read_catseyes_used(self, stream: core.Data):
    method write_catseyes_used (line 1170) | def write_catseyes_used(self, stream: core.Data):
    method write_fourth_forms (line 1175) | def write_fourth_forms(self, stream: core.Data):
    method read_favorites (line 1180) | def read_favorites(self, stream: core.Data):
    method write_favorites (line 1187) | def write_favorites(self, stream: core.Data):
    method read_chara_new_flags (line 1193) | def read_chara_new_flags(self, stream: core.Data):
    method write_chara_new_flags (line 1200) | def write_chara_new_flags(self, stream: core.Data):
    method read_talents (line 1206) | def read_talents(self, stream: core.Data):
    method write_talents (line 1216) | def write_talents(self, stream: core.Data):
    method serialize (line 1227) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 1236) | def deserialize(data: dict[str, Any]) -> Cats:
    method __repr__ (line 1246) | def __repr__(self) -> str:
    method __str__ (line 1249) | def __str__(self) -> str:

FILE: src/bcsfe/core/game/catbase/drop_chara.py
  class Drop (line 8) | class Drop:
  class CharaDrop (line 14) | class CharaDrop:
    method __init__ (line 15) | def __init__(self, save_file: core.SaveFile):
    method get_drops (line 19) | def get_drops(self) -> list[Drop] | None:
    method get_drop (line 37) | def get_drop(self, stage_id: int) -> Drop | None:
    method get_drops_from_chara_id (line 46) | def get_drops_from_chara_id(self, chara_id: int) -> list[Drop] | None:
    method unlock_drops_from_cat_id (line 56) | def unlock_drops_from_cat_id(self, cat_id: int) -> None:
    method remove_drops_from_cat_id (line 66) | def remove_drops_from_cat_id(self, cat_id: int) -> None:

FILE: src/bcsfe/core/game/catbase/gambling.py
  class GamblingEvent (line 8) | class GamblingEvent:
    method __init__ (line 9) | def __init__(
    method init (line 20) | def init() -> GamblingEvent:
    method read (line 24) | def read(data: core.Data, game_version: core.GameVersion) -> GamblingE...
    method write (line 61) | def write(self, data: core.Data, game_version: core.GameVersion):
    method serialize (line 85) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 93) | def deserialize(data: dict[str, Any]) -> GamblingEvent:
    method reset (line 100) | def reset(self):
    method reset_events (line 107) | def reset_events(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/catbase/gatya.py
  class Gatya (line 8) | class Gatya:
    method __init__ (line 9) | def __init__(self, rare_seed: int, normal_seed: int):
    method init (line 28) | def init() -> Gatya:
    method read_rare_normal_seed (line 32) | def read_rare_normal_seed(data: core.Data, gv: core.GameVersion) -> Ga...
    method read_event_seed (line 37) | def read_event_seed(self, data: core.Data, gv: core.GameVersion):
    method write_rare_normal_seed (line 43) | def write_rare_normal_seed(self, data: core.Data):
    method write_event_seed (line 47) | def write_event_seed(self, data: core.Data):
    method read2 (line 50) | def read2(self, data: core.Data):
    method write2 (line 60) | def write2(self, data: core.Data):
    method read_trade_progress (line 70) | def read_trade_progress(self, data: core.Data):
    method write_trade_progress (line 73) | def write_trade_progress(self, data: core.Data):
    method read_stepup (line 76) | def read_stepup(self, data: core.Data):
    method write_stepup (line 89) | def write_stepup(self, data: core.Data):
    method serialize (line 100) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 119) | def deserialize(data: dict[str, Any]) -> Gatya:
    method __repr__ (line 135) | def __repr__(self) -> str:
    method __str__ (line 138) | def __str__(self) -> str:
    method edit_rare_gatya_seed (line 141) | def edit_rare_gatya_seed(self):
    method edit_normal_gatya_seed (line 150) | def edit_normal_gatya_seed(self):
    method edit_event_gatya_seed (line 159) | def edit_event_gatya_seed(self):
    method read_gatya_data_set (line 168) | def read_gatya_data_set(self, save_file: core.SaveFile) -> GatyaDataSet:
  class GatyaDataSet (line 175) | class GatyaDataSet:
    method __init__ (line 176) | def __init__(self, save_file: core.SaveFile):
    method load_gatya_data_set (line 180) | def load_gatya_data_set(self, rarity: str, id: int) -> list[list[int]]...
    method get_cat_ids (line 197) | def get_cat_ids(self, gatya_id: int) -> list[int] | None:
  class GatyaInfo (line 206) | class GatyaInfo:
    method __init__ (line 207) | def __init__(self, gatya_id: int, cc: core.CountryCode, type_str: str ...
    method get_id_str (line 214) | def get_id_str(self) -> str:
    method get_cc_str (line 217) | def get_cc_str(self) -> str:
    method get_url (line 222) | def get_url(self) -> str:
    method download_data (line 225) | def download_data(self) -> core.Data | None:
    method get_file_path (line 236) | def get_file_path(self) -> core.Path:
    method save_data (line 246) | def save_data(self, data: core.Data):
    method load_data_from_file (line 253) | def load_data_from_file(self) -> core.Data | None:
    method get_data (line 258) | def get_data(self) -> core.Data | None:
    method get_name (line 266) | def get_name(self) -> str | None:
  class GatyaInfos (line 289) | class GatyaInfos:
    method __init__ (line 290) | def __init__(self, save_file: core.SaveFile, type_str: str = "R", set_...
    method get_all (line 300) | def get_all(
    method get (line 323) | def get(self, gatya_id: int, print_progress: bool):
    method get_info (line 335) | def get_info(self, gatya_id: int) -> GatyaInfo | None:
    method get_all_names (line 340) | def get_all_names(self) -> dict[int, str]:
  class GatyaDataOptionSet (line 354) | class GatyaDataOptionSet:
    method __init__ (line 355) | def __init__(
    method from_csv_row (line 378) | def from_csv_row(row: core.Row) -> GatyaDataOptionSet:
  class GatyaEventType (line 392) | class GatyaEventType(enum.Enum):
  class GatyaDataOption (line 398) | class GatyaDataOption:
    method __init__ (line 399) | def __init__(self, sets: list[GatyaDataOptionSet]):
    method get (line 402) | def get(self, set_id: int) -> GatyaDataOptionSet | None:
    method from_csv (line 410) | def from_csv(csv: core.CSV) -> GatyaDataOption:
    method from_data (line 419) | def from_data(data: core.Data) -> GatyaDataOption:
    method get_filename (line 423) | def get_filename(event_type: GatyaEventType) -> str:
    method read (line 427) | def read(

FILE: src/bcsfe/core/game/catbase/gatya_item.py
  class GatyaItemNames (line 6) | class GatyaItemNames:
    method __init__ (line 7) | def __init__(self, save_file: core.SaveFile):
    method __get_names (line 11) | def __get_names(self) -> list[str] | None:
    method get_name (line 25) | def get_name(self, index: int) -> str | None:
  class GatyaItemBuyItem (line 36) | class GatyaItemBuyItem:
    method __init__ (line 37) | def __init__(
  class GatyaItemCategory (line 67) | class GatyaItemCategory(enum.Enum):
  class GatyaItemBuy (line 82) | class GatyaItemBuy:
    method __init__ (line 83) | def __init__(self, save_file: core.SaveFile):
    method get_buy (line 87) | def get_buy(self) -> list[GatyaItemBuyItem] | None:
    method sort_by_index (line 118) | def sort_by_index(self, items: list[GatyaItemBuyItem]):
    method get_by_category (line 122) | def get_by_category(self, category: int | GatyaItemCategory) -> list[G...
    method get_names_by_category (line 131) | def get_names_by_category(self, category: int | GatyaItemCategory) -> ...
    method get (line 140) | def get(self, item_id: int) -> GatyaItemBuyItem | None:
    method get_by_server_id (line 148) | def get_by_server_id(self, server_id: int) -> GatyaItemBuyItem | None:

FILE: src/bcsfe/core/game/catbase/item_pack.py
  class PurchasedPack (line 6) | class PurchasedPack:
    method __init__ (line 7) | def __init__(self, purchased: bool):
    method init (line 11) | def init() -> PurchasedPack:
    method read (line 15) | def read(stream: core.Data) -> PurchasedPack:
    method write (line 19) | def write(self, stream: core.Data):
    method serialize (line 22) | def serialize(self) -> bool:
    method deserialize (line 26) | def deserialize(data: bool) -> PurchasedPack:
    method __repr__ (line 29) | def __repr__(self) -> str:
    method __str__ (line 32) | def __str__(self) -> str:
  class PurchaseSet (line 36) | class PurchaseSet:
    method __init__ (line 37) | def __init__(self, purchases: dict[str, PurchasedPack]):
    method init (line 41) | def init() -> PurchaseSet:
    method read (line 45) | def read(stream: core.Data) -> PurchaseSet:
    method write (line 53) | def write(self, stream: core.Data):
    method serialize (line 59) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 66) | def deserialize(data: dict[str, Any]) -> PurchaseSet:
    method __repr__ (line 74) | def __repr__(self) -> str:
    method __str__ (line 77) | def __str__(self) -> str:
  class Purchases (line 81) | class Purchases:
    method __init__ (line 82) | def __init__(self, purchases: dict[int, PurchaseSet]):
    method init (line 86) | def init() -> Purchases:
    method read (line 90) | def read(stream: core.Data) -> Purchases:
    method write (line 99) | def write(self, stream: core.Data):
    method serialize (line 105) | def serialize(self) -> dict[int, Any]:
    method deserialize (line 112) | def deserialize(data: dict[int, Any]) -> Purchases:
    method __repr__ (line 120) | def __repr__(self) -> str:
    method __str__ (line 123) | def __str__(self) -> str:
  class ItemPack (line 127) | class ItemPack:
    method __init__ (line 128) | def __init__(self, purchases: Purchases):
    method init (line 135) | def init() -> ItemPack:
    method read (line 139) | def read(stream: core.Data) -> ItemPack:
    method write (line 142) | def write(self, stream: core.Data):
    method read_displayed_packs (line 145) | def read_displayed_packs(self, stream: core.Data) -> None:
    method write_displayed_packs (line 154) | def write_displayed_packs(self, stream: core.Data) -> None:
    method read_three_days (line 160) | def read_three_days(self, stream: core.Data) -> None:
    method write_three_days (line 164) | def write_three_days(self, stream: core.Data) -> None:
    method serialize (line 168) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 177) | def deserialize(data: dict[str, Any]) -> ItemPack:
    method __repr__ (line 186) | def __repr__(self) -> str:
    method __str__ (line 189) | def __str__(self) -> str:

FILE: src/bcsfe/core/game/catbase/login_bonuses.py
  class Login (line 6) | class Login:
    method __init__ (line 7) | def __init__(self, count: int):
    method init (line 11) | def init() -> Login:
    method read (line 15) | def read(stream: core.Data) -> Login:
    method write (line 19) | def write(self, stream: core.Data):
    method serialize (line 22) | def serialize(self) -> int:
    method deserialize (line 26) | def deserialize(data: int) -> Login:
    method __repr__ (line 29) | def __repr__(self):
    method __str__ (line 32) | def __str__(self):
  class Logins (line 36) | class Logins:
    method __init__ (line 37) | def __init__(self, logins: list[Login]):
    method init (line 41) | def init() -> Logins:
    method read (line 45) | def read(stream: core.Data) -> Logins:
    method write (line 52) | def write(self, stream: core.Data):
    method serialize (line 57) | def serialize(self) -> list[int]:
    method deserialize (line 61) | def deserialize(data: list[int]) -> Logins:
    method __repr__ (line 64) | def __repr__(self):
    method __str__ (line 67) | def __str__(self):
  class LoginSets (line 71) | class LoginSets:
    method __init__ (line 72) | def __init__(self, logins: list[Logins]):
    method init (line 76) | def init() -> LoginSets:
    method read (line 80) | def read(stream: core.Data) -> LoginSets:
    method write (line 87) | def write(self, stream: core.Data):
    method serialize (line 92) | def serialize(self) -> list[list[int]]:
    method deserialize (line 96) | def deserialize(data: list[list[int]]) -> LoginSets:
    method __repr__ (line 99) | def __repr__(self):
    method __str__ (line 102) | def __str__(self):
  class LoginBonus (line 106) | class LoginBonus:
    method __init__ (line 107) | def __init__(
    method init (line 116) | def init(gv: core.GameVersion) -> LoginBonus:
    method read (line 123) | def read(stream: core.Data, gv: core.GameVersion) -> LoginBonus:
    method write (line 135) | def write(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 145) | def serialize(
    method deserialize (line 160) | def deserialize(data: dict[str, Any]) -> LoginBonus:
    method __repr__ (line 175) | def __repr__(self):
    method __str__ (line 178) | def __str__(self):
    method get_login (line 181) | def get_login(self, id: int) -> Login | None:

FILE: src/bcsfe/core/game/catbase/matatabi.py
  class Fruit (line 5) | class Fruit:
    method __init__ (line 6) | def __init__(
  class Matatabi (line 25) | class Matatabi:
    method __init__ (line 26) | def __init__(self, save_file: core.SaveFile):
    method __get_matatabi (line 33) | def __get_matatabi(self) -> list[Fruit] | None:
    method get_names (line 63) | def get_names(self) -> list[str | None] | None:

FILE: src/bcsfe/core/game/catbase/medals.py
  class Medals (line 7) | class Medals:
    method __init__ (line 8) | def __init__(
    method init (line 25) | def init() -> Medals:
    method read (line 29) | def read(data: core.Data) -> Medals:
    method write (line 44) | def write(self, data: core.Data) -> None:
    method serialize (line 56) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 67) | def deserialize(data: dict[str, Any]) -> Medals:
    method __repr__ (line 77) | def __repr__(self) -> str:
    method __str__ (line 84) | def __str__(self) -> str:
    method has_medal (line 87) | def has_medal(self, medal_id: int) -> bool:
    method edit_medals (line 91) | def edit_medals(save_file: core.SaveFile):
    method add_medal (line 136) | def add_medal(self, medal_id: int) -> None:
    method remove_medal (line 142) | def remove_medal(self, medal_id: int) -> None:
  class MedalNames (line 149) | class MedalNames:
    method __init__ (line 150) | def __init__(self, save_file: core.SaveFile):
    method get_medal_names (line 154) | def get_medal_names(self) -> list[list[str]] | None:
    method get_medal_name (line 166) | def get_medal_name(self, medal_id: int) -> list[str] | None:

FILE: src/bcsfe/core/game/catbase/mission.py
  class Mission (line 8) | class Mission:
    method __init__ (line 9) | def __init__(
    method init (line 30) | def init() -> Mission:
    method serialize (line 42) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 55) | def deserialize(data: dict[str, Any]) -> Mission:
    method __repr__ (line 67) | def __repr__(self):
    method __str__ (line 70) | def __str__(self):
  class Missions (line 74) | class Missions:
    method __init__ (line 75) | def __init__(
    method init (line 97) | def init() -> Missions:
    method read (line 110) | def read(stream: core.Data, gv: core.GameVersion) -> Missions:
    method write (line 138) | def write(self, stream: core.Data, gv: core.GameVersion):
    method read_weekly_missions (line 155) | def read_weekly_missions(self, stream: core.Data):
    method write_weekly_missions (line 161) | def write_weekly_missions(self, stream: core.Data):
    method serialize (line 167) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 181) | def deserialize(data: dict[str, Any]):
    method __repr__ (line 195) | def __repr__(self):
    method __str__ (line 198) | def __str__(self):
    method edit_missions (line 202) | def edit_missions(save_file: core.SaveFile):
  class MissionCondition (line 259) | class MissionCondition:
    method __init__ (line 260) | def __init__(
  class MissionConditions (line 275) | class MissionConditions:
    method __init__ (line 276) | def __init__(self, save: core.SaveFile):
    method get_conditions (line 280) | def get_conditions(self) -> dict[int, MissionCondition] | None:
    method get_condition (line 298) | def get_condition(self, mission_id: int) -> MissionCondition | None:
  class MissionNames (line 304) | class MissionNames:
    method __init__ (line 305) | def __init__(self, save: core.SaveFile):
    method get_names (line 309) | def get_names(self) -> dict[int, str] | None:

FILE: src/bcsfe/core/game/catbase/my_sale.py
  class MySale (line 6) | class MySale:
    method __init__ (line 7) | def __init__(self, dict_1: dict[int, int], dict_2: dict[int, bool]):
    method init (line 12) | def init() -> MySale:
    method read_bonus_hash (line 16) | def read_bonus_hash(stream: core.Data):
    method write_bonus_hash (line 33) | def write_bonus_hash(self, stream: core.Data):
    method serialize (line 44) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 51) | def deserialize(data: dict[str, Any]) -> MySale:
    method __repr__ (line 54) | def __repr__(self) -> str:
    method __str__ (line 57) | def __str__(self) -> str:

FILE: src/bcsfe/core/game/catbase/nyanko_club.py
  class NyankoClub (line 11) | class NyankoClub:
    method __init__ (line 12) | def __init__(
    method init (line 47) | def init() -> NyankoClub:
    method read (line 67) | def read(data: core.Data, gv: core.GameVersion) -> NyankoClub:
    method write (line 104) | def write(self, data: core.Data, gv: core.GameVersion):
    method serialize (line 122) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 142) | def deserialize(data: dict[str, Any]) -> NyankoClub:
    method __repr__ (line 161) | def __repr__(self):
    method __str__ (line 164) | def __str__(self):
    method get_gold_pass (line 167) | def get_gold_pass(
    method remove_gold_pass (line 206) | def remove_gold_pass(self, save_file: core.SaveFile):
    method get_random_officer_id (line 229) | def get_random_officer_id() -> int:
    method edit_gold_pass (line 233) | def edit_gold_pass(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/catbase/officer_pass.py
  class OfficerPass (line 7) | class OfficerPass:
    method __init__ (line 8) | def __init__(self, play_time: int):
    method init (line 15) | def init() -> OfficerPass:
    method read (line 19) | def read(data: core.Data) -> OfficerPass:
    method write (line 23) | def write(self, data: core.Data):
    method read_gold_pass (line 28) | def read_gold_pass(self, data: core.Data, gv: core.GameVersion):
    method write_gold_pass (line 31) | def write_gold_pass(self, data: core.Data, gv: core.GameVersion):
    method read_cat_data (line 34) | def read_cat_data(self, data: core.Data):
    method write_cat_data (line 38) | def write_cat_data(self, data: core.Data):
    method serialize (line 42) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 51) | def deserialize(data: dict[str, Any]) -> OfficerPass:
    method __repr__ (line 62) | def __repr__(self):
    method __str__ (line 65) | def __str__(self):
    method reset (line 68) | def reset(self, save_file: core.SaveFile):
    method fix_crash (line 75) | def fix_crash(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/catbase/playtime.py
  class PlayTime (line 9) | class PlayTime:
    method get_fps (line 13) | def get_fps() -> int:
    method seconds (line 17) | def seconds(self) -> int:
    method minutes (line 21) | def minutes(self) -> int:
    method hours (line 25) | def hours(self) -> int:
    method just_seconds (line 29) | def just_seconds(self) -> int:
    method just_minutes (line 33) | def just_minutes(self) -> int:
    method just_hours (line 37) | def just_hours(self) -> int:
    method from_hours (line 41) | def from_hours(hours: int) -> PlayTime:
    method from_minutes (line 45) | def from_minutes(minutes: int) -> PlayTime:
    method from_seconds (line 49) | def from_seconds(seconds: int) -> PlayTime:
    method from_hours_mins_secs (line 53) | def from_hours_mins_secs(
    method __add__ (line 62) | def __add__(self, other: PlayTime) -> PlayTime:
  function edit (line 66) | def edit(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/catbase/powerup.py
  class PowerUpHelper (line 6) | class PowerUpHelper:
    method __init__ (line 7) | def __init__(self, cat: core.Cat, save_file: core.SaveFile):
    method get_current_max_level (line 20) | def get_current_max_level(self) -> int | None:
    method has_strict_upgrade (line 28) | def has_strict_upgrade(self) -> bool:
    method get_upgrade_state_check (line 31) | def get_upgrade_state_check(self) -> int:
    method get_user_rank_check (line 36) | def get_user_rank_check(self) -> int:
    method __get_max_upgrade_level_check (line 41) | def __get_max_upgrade_level_check(self) -> int:
    method can_power_up (line 73) | def can_power_up(self) -> bool:
    method can_use_catseye (line 96) | def can_use_catseye(self) -> bool:
    method upgrade_cat (line 108) | def upgrade_cat(self, force: bool = False) -> bool:
    method get_max_max_base_upgrade_level (line 137) | def get_max_max_base_upgrade_level(self) -> int:
    method get_max_max_plus_upgrade_level (line 146) | def get_max_max_plus_upgrade_level(self) -> int:
    method get_max_possible_base (line 155) | def get_max_possible_base(self) -> int:
    method get_max_possible_plus (line 160) | def get_max_possible_plus(self) -> int:
    method reset_upgrade (line 165) | def reset_upgrade(self):
    method upgrade_by (line 169) | def upgrade_by(self, amount: int):
    method max_upgrade (line 177) | def max_upgrade(self):

FILE: src/bcsfe/core/game/catbase/scheme_items.py
  class SchemeDataItem (line 6) | class SchemeDataItem:
    method __init__ (line 7) | def __init__(
    method is_cat (line 33) | def is_cat(self) -> bool:
    method get_name (line 36) | def get_name(self, localizable: core.Localizable) -> str | None:
  class SchemeItems (line 44) | class SchemeItems:
    method __init__ (line 45) | def __init__(self, to_obtain: list[int], received: list[int]):
    method init (line 50) | def init() -> SchemeItems:
    method read (line 54) | def read(stream: core.Data) -> SchemeItems:
    method write (line 67) | def write(self, stream: core.Data):
    method serialize (line 76) | def serialize(self) -> dict[str, list[int]]:
    method deserialize (line 80) | def deserialize(data: dict[str, list[int]]) -> SchemeItems:
    method __repr__ (line 83) | def __repr__(self) -> str:
    method __str__ (line 86) | def __str__(self) -> str:
    method edit (line 89) | def edit(self, save_file: core.SaveFile):
    method add_scheme_items (line 155) | def add_scheme_items(
    method remove_scheme_items (line 175) | def remove_scheme_items(

FILE: src/bcsfe/core/game/catbase/special_skill.py
  class SpecialSkill (line 9) | class SpecialSkill:
    method __init__ (line 10) | def __init__(self, upg: core.Upgrade):
    method init (line 16) | def init() -> SpecialSkill:
    method read_upgrade (line 20) | def read_upgrade(stream: core.Data) -> SpecialSkill:
    method write_upgrade (line 24) | def write_upgrade(self, stream: core.Data):
    method read_seen (line 27) | def read_seen(self, stream: core.Data):
    method write_seen (line 30) | def write_seen(self, stream: core.Data):
    method read_max_upgrade_level (line 33) | def read_max_upgrade_level(self, stream: core.Data):
    method write_max_upgrade_level (line 37) | def write_max_upgrade_level(self, stream: core.Data):
    method serialize (line 40) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 48) | def deserialize(data: dict[str, Any]) -> SpecialSkill:
    method __repr__ (line 56) | def __repr__(self) -> str:
    method __str__ (line 59) | def __str__(self) -> str:
    method set_upgrade (line 62) | def set_upgrade(
  class SpecialSkills (line 83) | class SpecialSkills:
    method __init__ (line 84) | def __init__(self, skills: list[SpecialSkill]):
    method get_upgrade (line 87) | def get_upgrade(self, valid_skill_id: int) -> SpecialSkill:
    method set_upgrade (line 93) | def set_upgrade(
    method init (line 112) | def init() -> SpecialSkills:
    method get_valid_skills (line 116) | def get_valid_skills(self) -> list[SpecialSkill]:
    method read_upgrades (line 126) | def read_upgrades(stream: core.Data) -> SpecialSkills:
    method write_upgrades (line 135) | def write_upgrades(self, stream: core.Data):
    method read_gatya_seen (line 139) | def read_gatya_seen(self, stream: core.Data):
    method write_gatya_seen (line 143) | def write_gatya_seen(self, stream: core.Data):
    method read_max_upgrade_levels (line 147) | def read_max_upgrade_levels(self, stream: core.Data):
    method write_max_upgrade_levels (line 151) | def write_max_upgrade_levels(self, stream: core.Data):
    method serialize (line 155) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 159) | def deserialize(data: list[dict[str, Any]]) -> SpecialSkills:
    method __repr__ (line 166) | def __repr__(self) -> str:
    method __str__ (line 169) | def __str__(self) -> str:
    method edit (line 172) | def edit(self, save_file: core.SaveFile):
    method get_from_id (line 271) | def get_from_id(self, id: int, only_valid: bool = True) -> SpecialSkil...
  class AbilityDataItem (line 281) | class AbilityDataItem:
    method __init__ (line 282) | def __init__(
  class AbilityData (line 299) | class AbilityData:
    method __init__ (line 300) | def __init__(self, save_file: core.SaveFile):
    method get_ability_data (line 304) | def get_ability_data(self) -> list[AbilityDataItem] | None:
    method get_ability_data_item (line 324) | def get_ability_data_item(self, item_id: int) -> AbilityDataItem | None:

FILE: src/bcsfe/core/game/catbase/stamp.py
  class StampData (line 6) | class StampData:
    method __init__ (line 7) | def __init__(
    method init (line 20) | def init() -> StampData:
    method read (line 24) | def read(stream: core.Data) -> StampData:
    method write (line 31) | def write(self, stream: core.Data):
    method serialize (line 37) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 46) | def deserialize(data: dict[str, Any]) -> StampData:
    method __repr__ (line 54) | def __repr__(self):
    method __str__ (line 57) | def __str__(self):

FILE: src/bcsfe/core/game/catbase/talent_orbs.py
  class TalentOrb (line 7) | class TalentOrb:
    method __init__ (line 8) | def __init__(self, id: int, value: int):
    method init (line 13) | def init() -> TalentOrb:
    method read (line 20) | def read(stream: core.Data, gv: core.GameVersion) -> TalentOrb:
    method write (line 28) | def write(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 35) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 42) | def deserialize(data: dict[str, Any]) -> TalentOrb:
    method __repr__ (line 45) | def __repr__(self):
    method __str__ (line 48) | def __str__(self):
  class TalentOrbs (line 52) | class TalentOrbs:
    method __init__ (line 53) | def __init__(self, orbs: dict[int, TalentOrb]):
    method init (line 57) | def init() -> TalentOrbs:
    method read (line 61) | def read(stream: core.Data, gv: core.GameVersion) -> TalentOrbs:
    method write (line 69) | def write(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 74) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 78) | def deserialize(data: list[dict[str, Any]]) -> TalentOrbs:
    method __repr__ (line 83) | def __repr__(self):
    method __str__ (line 86) | def __str__(self):
    method set_orb (line 89) | def set_orb(self, id: int, value: int):
  class RawOrbInfo (line 93) | class RawOrbInfo:
    method __init__ (line 94) | def __init__(
  class OrbInfo (line 109) | class OrbInfo:
    method __init__ (line 110) | def __init__(
    method __str__ (line 122) | def __str__(self) -> str:
    method to_colortext (line 140) | def to_colortext(self) -> str:
    method create_unknown (line 149) | def create_unknown(orb_id: int) -> OrbInfo:
  class OrbInfoList (line 166) | class OrbInfoList:
    method __init__ (line 172) | def __init__(self, orb_info_list: list[OrbInfo]):
    method create (line 181) | def create(save_file: core.SaveFile) -> OrbInfoList | None:
    method parse_json_data (line 213) | def parse_json_data(json_data: core.Data) -> list[RawOrbInfo] | None:
    method load_names (line 236) | def load_names(
    method get_orb_info (line 269) | def get_orb_info(self, orb_id: int) -> OrbInfo | None:
    method get_orb_from_components (line 283) | def get_orb_from_components(
    method does_match_orb_str (line 304) | def does_match_orb_str(self, str_1: str | None, str_2: str | None) -> ...
    method get_orbs_from_component_fuzzy (line 315) | def get_orbs_from_component_fuzzy(
    method get_all_grades (line 341) | def get_all_grades(self) -> list[str]:
    method get_all_attributes (line 356) | def get_all_attributes(self) -> list[str | None]:
    method get_all_effects (line 377) | def get_all_effects(self) -> list[str]:
  class SaveOrb (line 395) | class SaveOrb:
    method __init__ (line 398) | def __init__(self, orb: OrbInfo, count: int):
  function color_from_enemy_type (line 409) | def color_from_enemy_type(target_id: int | None) -> str:
  function color_from_grade (line 439) | def color_from_grade(grade_id: int) -> str:
  function color_from_effect (line 453) | def color_from_effect(effect_id: int):
  class SaveOrbs (line 480) | class SaveOrbs:
    method __init__ (line 481) | def __init__(
    method from_save_file (line 496) | def from_save_file(save_file: core.SaveFile) -> SaveOrbs | None:
    method print (line 518) | def print(self):
    method sort_orbs (line 530) | def sort_orbs(self):
    method localize_attribute (line 538) | def localize_attribute(self, attribute: str | None) -> str | None:
    method edit (line 542) | def edit(self):
    method save (line 688) | def save(self, save_file: core.SaveFile):
    method edit_talent_orbs (line 698) | def edit_talent_orbs(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/catbase/unlock_popups.py
  class Popup (line 5) | class Popup:
    method __init__ (line 6) | def __init__(self, seen: bool):
    method init (line 10) | def init() -> Popup:
    method read (line 14) | def read(stream: core.Data) -> Popup:
    method write (line 18) | def write(self, stream: core.Data):
    method serialize (line 21) | def serialize(self) -> bool:
    method deserialize (line 25) | def deserialize(data: bool) -> Popup:
    method __repr__ (line 28) | def __repr__(self) -> str:
    method __str__ (line 31) | def __str__(self) -> str:
  class UnlockPopups (line 35) | class UnlockPopups:
    method __init__ (line 36) | def __init__(self, popups: dict[int, Popup]):
    method init (line 40) | def init() -> UnlockPopups:
    method read (line 44) | def read(stream: core.Data) -> UnlockPopups:
    method write (line 52) | def write(self, stream: core.Data):
    method serialize (line 58) | def serialize(self) -> dict[int, bool]:
    method deserialize (line 62) | def deserialize(data: dict[int, bool]) -> UnlockPopups:
    method __repr__ (line 67) | def __repr__(self) -> str:
    method __str__ (line 70) | def __str__(self) -> str:
  class UnlockPopupLine (line 74) | class UnlockPopupLine:
    method __init__ (line 75) | def __init__(
    method from_csv_row (line 118) | def from_csv_row(row: core.Row) -> UnlockPopupLine:
  class UnlockPopupData (line 142) | class UnlockPopupData:
    method __init__ (line 143) | def __init__(self, popups: list[UnlockPopupLine]):
    method from_csv (line 147) | def from_csv(csv: core.CSV) -> UnlockPopupData:
    method from_save (line 155) | def from_save(save_file: core.SaveFile) -> UnlockPopupData | None:

FILE: src/bcsfe/core/game/catbase/upgrade.py
  class Upgrade (line 8) | class Upgrade:
    method __init__ (line 9) | def __init__(self, plus: int, base: int):
    method get_base (line 16) | def get_base(self) -> int:
    method get_total (line 19) | def get_total(self) -> int:
    method get_plus (line 22) | def get_plus(self) -> int:
    method upgrade (line 25) | def upgrade(self):
    method increment_base (line 28) | def increment_base(self, amount: int):
    method increment_plus (line 31) | def increment_plus(self, amount: int):
    method get_random_base (line 34) | def get_random_base(self, max_base: int | None = None) -> int:
    method get_random_plus (line 42) | def get_random_plus(self, max_plus: int | None = None) -> int:
    method read (line 51) | def read(stream: core.Data) -> Upgrade:
    method write (line 57) | def write(self, stream: core.Data):
    method serialize (line 61) | def serialize(self) -> dict[str, Any]:
    method init (line 68) | def init() -> Upgrade:
    method reset (line 71) | def reset(self):
    method deserialize (line 76) | def deserialize(data: dict[str, Any]) -> Upgrade:
    method __repr__ (line 79) | def __repr__(self) -> str:
    method __str__ (line 82) | def __str__(self) -> str:
    method get_user_upgrade (line 86) | def get_user_upgrade(
    method copy (line 192) | def copy(self) -> Upgrade:

FILE: src/bcsfe/core/game/catbase/user_rank_rewards.py
  class RankGift (line 6) | class RankGift:
    method __init__ (line 7) | def __init__(
    method get_name (line 14) | def get_name(
  class RankGifts (line 20) | class RankGifts:
    method __init__ (line 21) | def __init__(self, save_file: core.SaveFile):
    method read_rank_gift (line 25) | def read_rank_gift(self) -> list[RankGift] | None:
    method get_rank_gift (line 42) | def get_rank_gift(self, user_rank: int) -> RankGift | None:
    method get_all_rank_gifts (line 50) | def get_all_rank_gifts(self, user_rank: int) -> list[RankGift] | None:
    method get_by_id (line 59) | def get_by_id(self, id: int) -> RankGift | None:
    method get_all_unlocked (line 66) | def get_all_unlocked(self, user_rank: int) -> list[RankGift] | None:
  class RankGiftDescription (line 77) | class RankGiftDescription:
    method __init__ (line 78) | def __init__(self, index: int, threshold: int, description: str):
  class RankGiftDescriptions (line 84) | class RankGiftDescriptions:
    method __init__ (line 85) | def __init__(self, save_file: core.SaveFile):
    method read_rank_gift_descriptions (line 89) | def read_rank_gift_descriptions(self) -> list[RankGiftDescription] | N...
    method get_name (line 102) | def get_name(self, user_rank: int) -> str | None:
  class Reward (line 111) | class Reward:
    method __init__ (line 112) | def __init__(self, claimed: bool):
    method init (line 116) | def init() -> Reward:
    method read (line 120) | def read(stream: core.Data) -> Reward:
    method write (line 123) | def write(self, stream: core.Data):
    method serialize (line 126) | def serialize(self) -> bool:
    method deserialize (line 130) | def deserialize(data: bool) -> Reward:
    method __repr__ (line 133) | def __repr__(self) -> str:
    method __str__ (line 136) | def __str__(self) -> str:
  class UserRankRewards (line 140) | class UserRankRewards:
    method __init__ (line 141) | def __init__(self, rewards: list[Reward]):
    method read_rank_gifts (line 145) | def read_rank_gifts(self, save_file: core.SaveFile) -> RankGifts:
    method init (line 151) | def init(gv: core.GameVersion) -> UserRankRewards:
    method read (line 160) | def read(stream: core.Data, gv: core.GameVersion) -> UserRankRewards:
    method write (line 170) | def write(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 176) | def serialize(self) -> list[bool]:
    method deserialize (line 180) | def deserialize(data: list[bool]) -> UserRankRewards:
    method __repr__ (line 183) | def __repr__(self) -> str:
    method __str__ (line 186) | def __str__(self) -> str:
    method set_claimed (line 189) | def set_claimed(self, index: int, claimed: bool):
    method edit (line 192) | def edit(self, save_file: core.SaveFile):
  function edit_user_rank_rewards (line 274) | def edit_user_rank_rewards(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/gamoto/base_materials.py
  class Material (line 6) | class Material:
    method __init__ (line 7) | def __init__(self, amount: int):
    method init (line 11) | def init() -> Material:
    method read (line 15) | def read(stream: core.Data) -> Material:
    method write (line 19) | def write(self, stream: core.Data):
    method serialize (line 22) | def serialize(self) -> int:
    method deserialize (line 26) | def deserialize(data: int) -> Material:
    method __repr__ (line 29) | def __repr__(self) -> str:
    method __str__ (line 32) | def __str__(self) -> str:
  class BaseMaterials (line 36) | class BaseMaterials:
    method __init__ (line 37) | def __init__(self, materials: list[Material]):
    method init (line 41) | def init() -> BaseMaterials:
    method read (line 45) | def read(stream: core.Data) -> BaseMaterials:
    method write (line 52) | def write(self, stream: core.Data):
    method serialize (line 57) | def serialize(self) -> list[int]:
    method deserialize (line 61) | def deserialize(data: list[int]) -> BaseMaterials:
    method __repr__ (line 66) | def __repr__(self) -> str:
    method __str__ (line 69) | def __str__(self) -> str:
    method edit_base_materials (line 72) | def edit_base_materials(self, save_file: core.SaveFile):

FILE: src/bcsfe/core/game/gamoto/cat_shrine.py
  class CatShrine (line 7) | class CatShrine:
    method __init__ (line 8) | def __init__(
    method init (line 26) | def init() -> CatShrine:
    method read (line 30) | def read(stream: core.Data) -> CatShrine:
    method write (line 39) | def write(self, stream: core.Data):
    method read_dialogs (line 48) | def read_dialogs(self, stream: core.Data):
    method write_dialogs (line 51) | def write_dialogs(self, stream: core.Data):
    method serialize (line 54) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 66) | def deserialize(data: dict[str, Any]) -> CatShrine:
    method __repr__ (line 78) | def __repr__(self):
    method __str__ (line 91) | def __str__(self):
    method edit_catshrine (line 95) | def edit_catshrine(save_file: core.SaveFile):
  class CatShrineLevels (line 167) | class CatShrineLevels:
    method __init__ (line 168) | def __init__(self, save_file: core.SaveFile):
    method get_boundaries (line 172) | def get_boundaries(self) -> list[int] | None:
    method get_level_from_xp (line 191) | def get_level_from_xp(self, xp: int) -> int | None:
    method get_xp_from_level (line 199) | def get_xp_from_level(self, level: int) -> int | None:
    method get_max_level (line 208) | def get_max_level(self) -> int | None:
    method get_max_xp (line 213) | def get_max_xp(self) -> int | None:

FILE: src/bcsfe/core/game/gamoto/catamins.py
  class Catamin (line 5) | class Catamin:
    method __init__ (line 6) | def __init__(self, amount: int):
    method read (line 10) | def read(stream: core.Data) -> Catamin:
    method write (line 14) | def write(self, stream: core.Data):
    method serialize (line 17) | def serialize(self) -> int:
    method deserialize (line 21) | def deserialize(data: int) -> Catamin:
    method __repr__ (line 24) | def __repr__(self):
    method __str__ (line 27) | def __str__(self):
  class Catamins (line 31) | class Catamins:
    method __init__ (line 32) | def __init__(self, catamins: list[Catamin]):
    method read (line 36) | def read(stream: core.Data) -> Catamins:
    method write (line 43) | def write(self, stream: core.Data):
    method serialize (line 48) | def serialize(self) -> list[int]:
    method deserialize (line 52) | def deserialize(data: list[int]) -> Catamins:
    method __repr__ (line 55) | def __repr__(self):
    method __str__ (line 58) | def __str__(self):

FILE: src/bcsfe/core/game/gamoto/gamatoto.py
  class MemberName (line 9) | class MemberName:
  class GamatotoMembersName (line 18) | class GamatotoMembersName:
    method __init__ (line 19) | def __init__(self, save_file: core.SaveFile):
    method read_members (line 23) | def read_members(self) -> list[MemberName] | None:
    method get_member (line 52) | def get_member(self, member_id: int) -> MemberName | None:
    method get_members_from_ids (line 60) | def get_members_from_ids(self, ids: list[int]) -> list[MemberName | No...
    method get_all_rarity (line 63) | def get_all_rarity(self, rarity: int) -> list[MemberName] | None:
    method get_members_from_helpers (line 69) | def get_members_from_helpers(
    method get_all_rarity_names (line 76) | def get_all_rarity_names(self) -> list[str] | None:
  class GamatotoLevel (line 86) | class GamatotoLevel:
  class GamatotoLimit (line 94) | class GamatotoLimit:
  class GamatotoLevels (line 100) | class GamatotoLevels:
    method __init__ (line 101) | def __init__(self, save_file: core.SaveFile):
    method read_levels (line 106) | def read_levels(self) -> list[GamatotoLevel] | None:
    method read_max_level (line 121) | def read_max_level(self) -> GamatotoLimit | None:
    method get_level (line 132) | def get_level(self, level: int) -> GamatotoLevel | None:
    method get_all_levels (line 139) | def get_all_levels(self) -> list[GamatotoLevel] | None:
    method get_level_from_xp (line 142) | def get_level_from_xp(self, xp: int) -> GamatotoLevel | None:
    method get_xp_from_level (line 156) | def get_xp_from_level(self, level: int) -> int | None:
    method get_max_level (line 164) | def get_max_level(self) -> int | None:
    method get_total_stages (line 169) | def get_total_stages(self) -> int | None:
    method get_total_helpers (line 174) | def get_total_helpers(self) -> int | None:
  class Helper (line 180) | class Helper:
    method __init__ (line 181) | def __init__(self, id: int):
    method init (line 185) | def init() -> Helper:
    method read (line 189) | def read(stream: core.Data) -> Helper:
    method write (line 193) | def write(self, stream: core.Data):
    method serialize (line 196) | def serialize(self) -> int:
    method deserialize (line 200) | def deserialize(data: int) -> Helper:
    method __repr__ (line 203) | def __repr__(self) -> str:
    method __str__ (line 206) | def __str__(self) -> str:
    method is_valid (line 209) | def is_valid(self) -> bool:
  class Helpers (line 213) | class Helpers:
    method __init__ (line 214) | def __init__(self, helpers: list[Helper]):
    method init (line 218) | def init() -> Helpers:
    method read (line 222) | def read(stream: core.Data) -> Helpers:
    method write (line 229) | def write(self, stream: core.Data):
    method serialize (line 234) | def serialize(self) -> list[int]:
    method deserialize (line 238) | def deserialize(data: list[int]) -> Helpers:
    method __repr__ (line 241) | def __repr__(self) -> str:
    method __str__ (line 244) | def __str__(self) -> str:
  class Gamatoto (line 248) | class Gamatoto:
    method __init__ (line 249) | def __init__(
    method init (line 273) | def init() -> Gamatoto:
    method read (line 285) | def read(stream: core.Data) -> Gamatoto:
    method write (line 303) | def write(self, stream: core.Data):
    method read_2 (line 312) | def read_2(self, stream: core.Data):
    method write_2 (line 316) | def write_2(self, stream: core.Data):
    method read_skin (line 320) | def read_skin(self, stream: core.Data):
    method write_skin (line 323) | def write_skin(self, stream: core.Data):
    method read_collab_data (line 326) | def read_collab_data(self, stream: core.Data):
    method write_collab_data (line 330) | def write_collab_data(self, stream: core.Data):
    method serialize (line 334) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 351) | def deserialize(data: dict[str, Any]) -> Gamatoto:
    method __repr__ (line 368) | def __repr__(self):
    method __str__ (line 379) | def __str__(self):
    method edit_xp (line 382) | def edit_xp(self, save_file: core.SaveFile):
    method edit_helpers (line 431) | def edit_helpers(self, save_file: core.SaveFile):
  function edit_xp (line 491) | def edit_xp(save_file: core.SaveFile):
  function edit_helpers (line 495) | def edit_helpers(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/gamoto/ototo.py
  class LevelPartRecipeUnlock (line 9) | class LevelPartRecipeUnlock:
  class CastleRecipeUnlock (line 18) | class CastleRecipeUnlock:
    method __init__ (line 19) | def __init__(self, save_file: core.SaveFile):
    method get_recipe_unlocks (line 23) | def get_recipe_unlocks(self) -> list[LevelPartRecipeUnlock] | None:
    method get_recipe_unlock (line 44) | def get_recipe_unlock(self, index: int) -> LevelPartRecipeUnlock | None:
    method get_max_level (line 53) | def get_max_level(self, cannon_id: int, part_id: int) -> int | None:
    method get_max_part_level (line 68) | def get_max_part_level(self, part_id: int) -> int | None:
  class CannonDescription (line 81) | class CannonDescription:
    method get_part_names (line 99) | def get_part_names(self) -> list[str]:
    method get_part_name (line 109) | def get_part_name(self, index: int) -> str:
    method get_longest_part_name (line 112) | def get_longest_part_name(self) -> str:
    method get_cannon_name (line 115) | def get_cannon_name(self) -> str:
  class CannonDescriptions (line 119) | class CannonDescriptions:
    method __init__ (line 120) | def __init__(self, save_file: core.SaveFile):
    method get_cannon_descriptions (line 124) | def get_cannon_descriptions(self) -> list[CannonDescription] | None:
    method get_cannon_description (line 159) | def get_cannon_description(
    method get_longest_longest_part_name (line 170) | def get_longest_longest_part_name(self) -> str | None:
  class Cannon (line 182) | class Cannon:
    method __init__ (line 183) | def __init__(self, development: int, levels: list[int]):
    method init (line 188) | def init() -> Cannon:
    method read (line 192) | def read(stream: core.Data) -> Cannon:
    method write (line 200) | def write(self, stream: core.Data):
    method serialize (line 206) | def serialize(self) -> list[int]:
    method deserialize (line 210) | def deserialize(data: list[int]) -> Cannon:
    method __repr__ (line 213) | def __repr__(self):
    method __str__ (line 216) | def __str__(self):
  class Cannons (line 220) | class Cannons:
    method __init__ (line 221) | def __init__(
    method init (line 228) | def init(gv: core.GameVersion) -> Cannons:
    method read (line 242) | def read(stream: core.Data, gv: core.GameVersion) -> Cannons:
    method write (line 263) | def write(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 279) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 289) | def deserialize(data: dict[str, Any]) -> Cannons:
    method __repr__ (line 298) | def __repr__(self):
    method __str__ (line 301) | def __str__(self):
  class Ototo (line 305) | class Ototo:
    method __init__ (line 306) | def __init__(
    method init (line 319) | def init(game_version: core.GameVersion) -> Ototo:
    method read (line 323) | def read(stream: core.Data) -> Ototo:
    method write (line 327) | def write(self, stream: core.Data):
    method read_2 (line 330) | def read_2(self, stream: core.Data, gv: core.GameVersion):
    method write_2 (line 337) | def write_2(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 347) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 358) | def deserialize(data: dict[str, Any]) -> Ototo:
    method __repr__ (line 369) | def __repr__(self):
    method __str__ (line 372) | def __str__(self):
    method get_max_engineers (line 376) | def get_max_engineers(save_file: core.SaveFile) -> int:
    method edit_engineers (line 385) | def edit_engineers(self, save_file: core.SaveFile):
    method display_current_cannons (line 399) | def display_current_cannons(
    method edit_cannon (line 458) | def edit_cannon(self, save_file: core.SaveFile):
    method select_development (line 506) | def select_development(self) -> int | None:
    method edit_cannon_development (line 513) | def edit_cannon_development(
    method edit_cannon_level (line 550) | def edit_cannon_level(
    method get_cannon (line 639) | def get_cannon(self, cannon_id: int) -> Cannon | None:
    method get_stage_name (line 645) | def get_stage_name(development: int) -> str:
  function edit_cannon (line 659) | def edit_cannon(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/localizable.py
  class Localizable (line 5) | class Localizable:
    method __init__ (line 6) | def __init__(self, save_file: core.SaveFile):
    method get_localizable (line 10) | def get_localizable(self) -> dict[str, str] | None:
    method get (line 25) | def get(self, key: str) -> str | None:
    method get_lang (line 30) | def get_lang(self) -> str | None:

FILE: src/bcsfe/core/game/map/aku.py
  class Stage (line 7) | class Stage:
    method __init__ (line 8) | def __init__(self, clear_times: int):
    method init (line 12) | def init() -> Stage:
    method read (line 16) | def read(data: core.Data) -> Stage:
    method write (line 20) | def write(self, data: core.Data):
    method serialize (line 23) | def serialize(self) -> int:
    method deserialize (line 27) | def deserialize(data: int) -> Stage:
    method __repr__ (line 32) | def __repr__(self):
    method __str__ (line 35) | def __str__(self):
    method clear_stage (line 38) | def clear_stage(self, clear_count: int = 1):
  class Chapter (line 42) | class Chapter:
    method __init__ (line 43) | def __init__(self, current_stage: int, total_stages: int = 0):
    method init (line 48) | def init(total_stages: int) -> Chapter:
    method read_current_stage (line 52) | def read_current_stage(data: core.Data):
    method write_current_stage (line 56) | def write_current_stage(self, data: core.Data):
    method read_stages (line 59) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 62) | def write_stages(self, data: core.Data):
    method serialize (line 66) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 73) | def deserialize(data: dict[str, Any]) -> Chapter:
    method __repr__ (line 80) | def __repr__(self):
    method __str__ (line 83) | def __str__(self):
  class ChaptersStars (line 87) | class ChaptersStars:
    method __init__ (line 88) | def __init__(self, chapters: list[Chapter]):
    method init (line 92) | def init(total_stages: int, total_stars: int) -> ChaptersStars:
    method read_current_stage (line 98) | def read_current_stage(data: core.Data, total_stars: int):
    method write_current_stage (line 104) | def write_current_stage(self, data: core.Data):
    method read_stages (line 108) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 112) | def write_stages(self, data: core.Data):
    method serialize (line 116) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 120) | def deserialize(data: list[dict[str, Any]]) -> ChaptersStars:
    method __repr__ (line 124) | def __repr__(self):
    method __str__ (line 127) | def __str__(self):
  class AkuChapters (line 131) | class AkuChapters:
    method __init__ (line 132) | def __init__(self, chapters: list[ChaptersStars]):
    method init (line 136) | def init() -> AkuChapters:
    method read (line 140) | def read(data: core.Data) -> AkuChapters:
    method write (line 155) | def write(self, data: core.Data):
    method serialize (line 172) | def serialize(self) -> list[list[dict[str, Any]]]:
    method deserialize (line 176) | def deserialize(data: list[list[dict[str, Any]]]) -> AkuChapters:
    method __repr__ (line 180) | def __repr__(self):
    method __str__ (line 183) | def __str__(self):
    method edit_aku_chapters (line 187) | def edit_aku_chapters(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/map/challenge.py
  class ChallengeChapters (line 7) | class ChallengeChapters:
    method __init__ (line 8) | def __init__(self, chapters: core.Chapters):
    method init (line 14) | def init() -> ChallengeChapters:
    method read (line 18) | def read(data: core.Data) -> ChallengeChapters:
    method write (line 22) | def write(self, data: core.Data):
    method read_scores (line 25) | def read_scores(self, data: core.Data):
    method write_scores (line 29) | def write_scores(self, data: core.Data):
    method read_popup (line 34) | def read_popup(self, data: core.Data):
    method write_popup (line 37) | def write_popup(self, data: core.Data):
    method serialize (line 40) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 48) | def deserialize(data: dict[str, Any]) -> ChallengeChapters:
    method __repr__ (line 56) | def __repr__(self):
    method __str__ (line 59) | def __str__(self):
    method edit_score (line 62) | def edit_score(self):
  function edit_challenge_score (line 72) | def edit_challenge_score(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/map/chapters.py
  class Stage (line 7) | class Stage:
    method __init__ (line 8) | def __init__(self, clear_times: int):
    method init (line 12) | def init() -> Stage:
    method read (line 16) | def read(data: core.Data) -> Stage:
    method write (line 20) | def write(self, data: core.Data):
    method serialize (line 23) | def serialize(self) -> int:
    method deserialize (line 27) | def deserialize(data: int) -> Stage:
    method __repr__ (line 32) | def __repr__(self):
    method __str__ (line 35) | def __str__(self):
    method clear_stage (line 38) | def clear_stage(self, clear_amount: int = 1, ensure_cleared_only: bool...
    method unclear_stage (line 44) | def unclear_stage(self):
  class Chapter (line 48) | class Chapter:
    method __init__ (line 49) | def __init__(self, selected_stage: int, total_stages: int = 0):
    method clear_stage (line 57) | def clear_stage(
    method unclear_stage (line 74) | def unclear_stage(self, index: int):
    method init (line 80) | def init(total_stages: int) -> Chapter:
    method read_selected_stage (line 84) | def read_selected_stage(data: core.Data) -> Chapter:
    method write_selected_stage (line 88) | def write_selected_stage(self, data: core.Data):
    method read_clear_progress (line 91) | def read_clear_progress(self, data: core.Data):
    method write_clear_progress (line 94) | def write_clear_progress(self, data: core.Data):
    method read_stages (line 97) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 100) | def write_stages(self, data: core.Data):
    method read_chapter_unlock_state (line 104) | def read_chapter_unlock_state(self, data: core.Data):
    method write_chapter_unlock_state (line 107) | def write_chapter_unlock_state(self, data: core.Data):
    method serialize (line 110) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 119) | def deserialize(data: dict[str, Any]) -> Chapter:
    method __repr__ (line 126) | def __repr__(self):
    method __str__ (line 129) | def __str__(self):
  class ChaptersStars (line 133) | class ChaptersStars:
    method __init__ (line 134) | def __init__(self, chapters: list[Chapter]):
    method clear_stage (line 137) | def clear_stage(
    method unclear_stage (line 153) | def unclear_stage(self, star: int, stage: int):
    method init (line 161) | def init(total_stages: int, total_stars: int) -> ChaptersStars:
    method read_selected_stage (line 166) | def read_selected_stage(data: core.Data, total_stars: int) -> Chapters...
    method write_selected_stage (line 170) | def write_selected_stage(self, data: core.Data):
    method read_clear_progress (line 174) | def read_clear_progress(self, data: core.Data):
    method write_clear_progress (line 178) | def write_clear_progress(self, data: core.Data):
    method read_stages (line 182) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 187) | def write_stages(self, data: core.Data):
    method read_chapter_unlock_state (line 192) | def read_chapter_unlock_state(self, data: core.Data):
    method write_chapter_unlock_state (line 196) | def write_chapter_unlock_state(self, data: core.Data):
    method serialize (line 200) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 204) | def deserialize(data: list[dict[str, Any]]) -> ChaptersStars:
    method __repr__ (line 208) | def __repr__(self):
    method __str__ (line 211) | def __str__(self):
  class Chapters (line 215) | class Chapters:
    method __init__ (line 216) | def __init__(self, chapters: list[ChaptersStars]):
    method get_total_stars (line 219) | def get_total_stars(self, map: int) -> int:
    method get_total_stages (line 222) | def get_total_stages(self, map: int, star: int) -> int:
    method clear_stage (line 225) | def clear_stage(
    method unclear_stage (line 242) | def unclear_stage(self, map: int, star: int, stage: int) -> bool:
    method init (line 251) | def init() -> Chapters:
    method read (line 255) | def read(data: core.Data, read_every_time: bool = True) -> Chapters:
    method get_lengths (line 296) | def get_lengths(self) -> tuple[int, int, int]:
    method write (line 309) | def write(self, data: core.Data, write_every_time: bool = True):
    method serialize (line 340) | def serialize(self) -> list[list[dict[str, Any]]]:
    method deserialize (line 344) | def deserialize(data: list[list[dict[str, Any]]]) -> Chapters:
    method __repr__ (line 349) | def __repr__(self):
    method __str__ (line 352) | def __str__(self):
    method unclear_rest (line 355) | def unclear_rest(self, stages: list[int], stars: int, id: int):
    method edit_chapters (line 363) | def edit_chapters(
    method set_total_stages (line 370) | def set_total_stages(self, map: int, total_stages: int):

FILE: src/bcsfe/core/game/map/dojo.py
  class Stage (line 7) | class Stage:
    method __init__ (line 8) | def __init__(self, score: int):
    method init (line 12) | def init() -> Stage:
    method read (line 16) | def read(stream: core.Data) -> Stage:
    method write (line 20) | def write(self, stream: core.Data):
    method serialize (line 23) | def serialize(self) -> int:
    method deserialize (line 27) | def deserialize(data: int) -> Stage:
    method __repr__ (line 30) | def __repr__(self) -> str:
    method __str__ (line 33) | def __str__(self) -> str:
  class Chapter (line 37) | class Chapter:
    method __init__ (line 38) | def __init__(self, stages: dict[int, Stage]):
    method get_stage (line 41) | def get_stage(self, stage_id: int) -> Stage:
    method init (line 47) | def init() -> Chapter:
    method read (line 51) | def read(stream: core.Data) -> Chapter:
    method write (line 61) | def write(self, stream: core.Data):
    method serialize (line 67) | def serialize(self) -> dict[int, Any]:
    method deserialize (line 71) | def deserialize(data: dict[int, Any]) -> Chapter:
    method __repr__ (line 76) | def __repr__(self) -> str:
    method __str__ (line 79) | def __str__(self) -> str:
  class Chapters (line 83) | class Chapters:
    method __init__ (line 84) | def __init__(self, chapters: dict[int, Chapter]):
    method get_stage (line 87) | def get_stage(self, chapter_id: int, stage_id: int) -> Stage:
    method init (line 93) | def init() -> Chapters:
    method read (line 97) | def read(stream: core.Data) -> Chapters:
    method write (line 107) | def write(self, stream: core.Data):
    method serialize (line 113) | def serialize(self) -> dict[int, Any]:
    method deserialize (line 120) | def deserialize(data: dict[int, Any]) -> Chapters:
    method __repr__ (line 128) | def __repr__(self) -> str:
    method __str__ (line 131) | def __str__(self) -> str:
  class Ranking (line 135) | class Ranking:
    method __init__ (line 136) | def __init__(
    method init (line 166) | def init() -> Ranking:
    method read (line 183) | def read(stream: core.Data, game_version: core.GameVersion) -> Ranking:
    method write (line 216) | def write(self, stream: core.Data, game_version: core.GameVersion):
    method read_did_win_rewards (line 232) | def read_did_win_rewards(self, stream: core.Data):
    method write_did_win_rewards (line 235) | def write_did_win_rewards(self, stream: core.Data):
    method serialize (line 238) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 256) | def deserialize(data: dict[str, Any]) -> Ranking:
    method __repr__ (line 274) | def __repr__(self) -> str:
    method __str__ (line 287) | def __str__(self) -> str:
  class Dojo (line 291) | class Dojo:
    method __init__ (line 292) | def __init__(self, chapters: Chapters):
    method init (line 299) | def init() -> Dojo:
    method read_chapters (line 303) | def read_chapters(stream: core.Data) -> Dojo:
    method write_chapters (line 307) | def write_chapters(self, stream: core.Data):
    method read_item_locks (line 310) | def read_item_locks(self, stream: core.Data):
    method write_item_locks (line 314) | def write_item_locks(self, stream: core.Data):
    method read_ranking (line 318) | def read_ranking(self, stream: core.Data, game_version: core.GameVersi...
    method write_ranking (line 321) | def write_ranking(self, stream: core.Data, game_version: core.GameVers...
    method serialize (line 324) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 333) | def deserialize(data: dict[str, Any]) -> Dojo:
    method __repr__ (line 343) | def __repr__(self) -> str:
    method __str__ (line 346) | def __str__(self) -> str:
    method edit_score (line 349) | def edit_score(self):
  function edit_dojo_score (line 359) | def edit_dojo_score(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/map/enigma.py
  class Stage (line 8) | class Stage:
    method __init__ (line 9) | def __init__(
    method init (line 22) | def init() -> Stage:
    method read (line 26) | def read(data: core.Data) -> Stage:
    method write (line 34) | def write(self, data: core.Data):
    method serialize (line 40) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 49) | def deserialize(data: dict[str, Any]) -> Stage:
    method __repr__ (line 57) | def __repr__(self):
    method __str__ (line 60) | def __str__(self):
  class Enigma (line 64) | class Enigma:
    method __init__ (line 65) | def __init__(
    method init (line 84) | def init() -> Enigma:
    method read (line 88) | def read(data: core.Data, game_version: core.GameVersion) -> Enigma:
    method write (line 120) | def write(self, data: core.Data, game_version: core.GameVersion):
    method serialize (line 138) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 150) | def deserialize(data: dict[str, Any]) -> Enigma:
    method __repr__ (line 161) | def __repr__(self):
    method __str__ (line 164) | def __str__(self):
    method edit_enigma (line 167) | def edit_enigma(self, save_file: core.SaveFile):
  function edit_enigma (line 223) | def edit_enigma(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/map/event.py
  class EventStage (line 7) | class EventStage:
    method __init__ (line 8) | def __init__(self, clear_amount: int):
    method init (line 12) | def init() -> EventStage:
    method read (line 16) | def read(data: core.Data, is_int: bool) -> EventStage:
    method write (line 23) | def write(self, data: core.Data, is_int: bool):
    method serialize (line 29) | def serialize(self) -> int:
    method deserialize (line 33) | def deserialize(data: int) -> EventStage:
    method __repr__ (line 38) | def __repr__(self) -> str:
    method __str__ (line 41) | def __str__(self) -> str:
    method clear_stage (line 44) | def clear_stage(self, clear_amount: int = 1, ensure_cleared_only: bool...
    method unclear_stage (line 50) | def unclear_stage(self):
  class EventSubChapter (line 54) | class EventSubChapter:
    method __init__ (line 55) | def __init__(self, selected_stage: int, total_stages: int = 0):
    method clear_stage (line 61) | def clear_stage(
    method unclear_stage (line 78) | def unclear_stage(self, index: int) -> bool:
    method clear_map (line 84) | def clear_map(self, increment: bool = True) -> bool:
    method init (line 96) | def init(total_stages: int) -> EventSubChapter:
    method read_selected_stage (line 100) | def read_selected_stage(data: core.Data, is_int: bool) -> EventSubChap...
    method write_selected_stage (line 107) | def write_selected_stage(self, data: core.Data, is_int: bool):
    method read_clear_progress (line 113) | def read_clear_progress(self, data: core.Data, is_int: bool):
    method write_clear_progress (line 119) | def write_clear_progress(self, data: core.Data, is_int: bool):
    method read_stages (line 125) | def read_stages(self, data: core.Data, total_stages: int, is_int: bool):
    method write_stages (line 128) | def write_stages(self, data: core.Data, is_int: bool):
    method read_chapter_unlock_state (line 132) | def read_chapter_unlock_state(self, data: core.Data, is_int: bool):
    method write_chapter_unlock_state (line 138) | def write_chapter_unlock_state(self, data: core.Data, is_int: bool):
    method serialize (line 144) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 153) | def deserialize(data: dict[str, Any]) -> EventSubChapter:
    method __repr__ (line 164) | def __repr__(self) -> str:
    method __str__ (line 167) | def __str__(self) -> str:
  class EventSubChapterStars (line 171) | class EventSubChapterStars:
    method __init__ (line 172) | def __init__(self, chapters: list[EventSubChapter]):
    method clear_stage (line 176) | def clear_stage(
    method unclear_stage (line 192) | def unclear_stage(self, star: int, stage: int):
    method clear_map (line 199) | def clear_map(self, star: int, increment: bool = True) -> bool:
    method clear_chapter (line 206) | def clear_chapter(self, increment: bool = True) -> bool:
    method init (line 212) | def init(total_stars: int) -> EventSubChapterStars:
    method read_selected_stage (line 218) | def read_selected_stage(
    method write_selected_stage (line 227) | def write_selected_stage(self, data: core.Data, is_int: bool):
    method read_clear_progress (line 231) | def read_clear_progress(self, data: core.Data, is_int: bool):
    method write_clear_progress (line 235) | def write_clear_progress(self, data: core.Data, is_int: bool):
    method read_stages (line 239) | def read_stages(self, data: core.Data, total_stages: int, is_int: bool):
    method write_stages (line 245) | def write_stages(self, data: core.Data, is_int: bool):
    method read_chapter_unlock_state (line 251) | def read_chapter_unlock_state(self, data: core.Data, is_int: bool):
    method write_chapter_unlock_state (line 255) | def write_chapter_unlock_state(self, data: core.Data, is_int: bool):
    method read_legend_restrictions (line 259) | def read_legend_restrictions(self, data: core.Data):
    method write_legend_restrictions (line 262) | def write_legend_restrictions(self, data: core.Data):
    method serialize (line 265) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 272) | def deserialize(data: dict[str, Any]) -> EventSubChapterStars:
    method __repr__ (line 280) | def __repr__(self) -> str:
    method __str__ (line 283) | def __str__(self) -> str:
  class EventChapterGroup (line 287) | class EventChapterGroup:
    method __init__ (line 288) | def __init__(self, chapters: list[EventSubChapterStars]):
    method clear_stage (line 291) | def clear_stage(
    method unclear_stage (line 312) | def unclear_stage(self, map: int, star: int, stage: int) -> bool:
    method clear_map (line 320) | def clear_map(self, map: int, star: int, increment: bool = True):
    method clear_chapter (line 325) | def clear_chapter(self, map: int, increment: bool = True):
    method clear_group (line 330) | def clear_group(self, increment: bool = True):
    method init (line 335) | def init(total_subchapters: int, total_stars: int) -> EventChapterGroup:
    method read_selected_stage (line 341) | def read_selected_stage(
    method write_selected_stage (line 350) | def write_selected_stage(self, data: core.Data, is_int: bool):
    method read_clear_progress (line 354) | def read_clear_progress(self, data: core.Data, is_int: bool):
    method write_clear_progress (line 358) | def write_clear_progress(self, data: core.Data, is_int: bool):
    method read_stages (line 362) | def read_stages(self, data: core.Data, total_stages: int, is_int: bool):
    method write_stages (line 366) | def write_stages(self, data: core.Data, is_int: bool):
    method read_chapter_unlock_state (line 370) | def read_chapter_unlock_state(self, data: core.Data, is_int: bool):
    method write_chapter_unlock_state (line 374) | def write_chapter_unlock_state(self, data: core.Data, is_int: bool):
    method read_legend_restrictions (line 378) | def read_legend_restrictions(self, data: core.Data):
    method write_legend_restrictions (line 382) | def write_legend_restrictions(self, data: core.Data):
    method serialize (line 386) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 390) | def deserialize(data: list[dict[str, Any]]) -> EventChapterGroup:
    method __repr__ (line 394) | def __repr__(self) -> str:
    method __str__ (line 397) | def __str__(self) -> str:
  class EventChapters (line 401) | class EventChapters:
    method __init__ (line 402) | def __init__(self, chapters: list[EventChapterGroup]):
    method clear_stage (line 409) | def clear_stage(
    method unclear_stage (line 428) | def unclear_stage(self, type: int, map: int, star: int, stage: int) ->...
    method clear_map (line 431) | def clear_map(self, type: int, map: int, star: int, increment: bool = ...
    method clear_chapter (line 434) | def clear_chapter(self, type: int, map: int, increment: bool = True):
    method clear_group (line 437) | def clear_group(self, type: int, increment: bool = True):
    method init (line 441) | def init(gv: core.GameVersion) -> EventChapters:
    method read (line 465) | def read(data: core.Data, gv: core.GameVersion) -> EventChapters:
    method get_lengths (line 564) | def get_lengths(self) -> tuple[int, int, int, int]:
    method write (line 587) | def write(self, data: core.Data, gv: core.GameVersion):
    method read_legend_restrictions (line 655) | def read_legend_restrictions(self, data: core.Data, gv: core.GameVersi...
    method write_legend_restrictions (line 671) | def write_legend_restrictions(self, data: core.Data, gv: core.GameVers...
    method read_dicts (line 684) | def read_dicts(self, data: core.Data):
    method write_dicts (line 690) | def write_dicts(self, data: core.Data):
    method serialize (line 696) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 706) | def deserialize(data: dict[str, Any]) -> EventChapters:
    method __repr__ (line 718) | def __repr__(self) -> str:
    method __str__ (line 721) | def __str__(self) -> str:
    method get_total_stars (line 724) | def get_total_stars(self, type: int, map: int) -> int:
    method get_total_stages (line 730) | def get_total_stages(self, type: int, map: int, star: int) -> int:
    method ask_stars (line 737) | def ask_stars(
    method ask_stars_unclear (line 750) | def ask_stars_unclear(
    method get_stage_names (line 761) | def get_stage_names(map_names: core.MapNames, chapter_id: int) -> list...
    method ask_stages (line 773) | def ask_stages(map_names: core.MapNames, chapter_id: int) -> list[int]...
    method ask_stages_stage_names (line 790) | def ask_stages_stage_names(stage_names: list[str]) -> list[int] | None:
    method ask_stages_stage_names_one (line 797) | def ask_stages_stage_names_one(stage_names: list[str]) -> int | None:
    method ask_clear_amount (line 812) | def ask_clear_amount() -> int | None:
    method edit_sol_chapters (line 820) | def edit_sol_chapters(save_file: core.SaveFile):
    method edit_event_chapters (line 824) | def edit_event_chapters(save_file: core.SaveFile):
    method edit_collab_chapters (line 828) | def edit_collab_chapters(save_file: core.SaveFile):
    method select_map_names (line 832) | def select_map_names(names_dict: dict[int, str | None]) -> list[int] |...
    method print_current_chapter (line 925) | def print_current_chapter(name: str | None, id: int):
    method print_current_stage (line 933) | def print_current_stage(name: str | None, index: int):
    method edit_chapters (line 941) | def edit_chapters(
    method unclear_rest (line 952) | def unclear_rest(

FILE: src/bcsfe/core/game/map/ex_stage.py
  class Stage (line 5) | class Stage:
    method __init__ (line 6) | def __init__(self, clear_amount: int):
    method init (line 10) | def init() -> Stage:
    method read (line 14) | def read(stream: core.Data) -> Stage:
    method write (line 18) | def write(self, stream: core.Data):
    method serialize (line 21) | def serialize(self) -> int:
    method deserialize (line 25) | def deserialize(data: int) -> Stage:
    method __repr__ (line 28) | def __repr__(self) -> str:
    method __str__ (line 31) | def __str__(self) -> str:
  class Chapter (line 35) | class Chapter:
    method __init__ (line 36) | def __init__(self, stages: list[Stage]):
    method init (line 40) | def init() -> Chapter:
    method read (line 44) | def read(stream: core.Data) -> Chapter:
    method write (line 51) | def write(self, stream: core.Data):
    method serialize (line 55) | def serialize(self) -> list[int]:
    method deserialize (line 59) | def deserialize(data: list[int]) -> Chapter:
    method __repr__ (line 62) | def __repr__(self) -> str:
    method __str__ (line 65) | def __str__(self) -> str:
  class ExChapters (line 69) | class ExChapters:
    method __init__ (line 70) | def __init__(self, chapters: list[Chapter]):
    method init (line 74) | def init() -> ExChapters:
    method read (line 78) | def read(stream: core.Data) -> ExChapters:
    method write (line 86) | def write(self, stream: core.Data):
    method serialize (line 91) | def serialize(self) -> list[list[int]]:
    method deserialize (line 95) | def deserialize(data: list[list[int]]) -> ExChapters:
    method __repr__ (line 98) | def __repr__(self) -> str:
    method __str__ (line 101) | def __str__(self) -> str:

FILE: src/bcsfe/core/game/map/gauntlets.py
  class Stage (line 7) | class Stage:
    method __init__ (line 8) | def __init__(self, clear_times: int):
    method init (line 12) | def init() -> Stage:
    method read (line 16) | def read(data: core.Data) -> Stage:
    method write (line 20) | def write(self, data: core.Data):
    method serialize (line 23) | def serialize(self) -> int:
    method deserialize (line 27) | def deserialize(data: int) -> Stage:
    method __repr__ (line 32) | def __repr__(self):
    method __str__ (line 35) | def __str__(self):
    method clear_stage (line 38) | def clear_stage(self, clear_amount: int = 1, ensure_cleared_only: bool...
    method unclear_stage (line 44) | def unclear_stage(self):
  class Chapter (line 48) | class Chapter:
    method __init__ (line 49) | def __init__(self, selected_stage: int, total_stages: int = 0):
    method clear_stage (line 56) | def clear_stage(
    method unclear_stage (line 73) | def unclear_stage(self, index: int):
    method init (line 79) | def init(total_stages: int) -> Chapter:
    method read_selected_stage (line 83) | def read_selected_stage(data: core.Data) -> Chapter:
    method write_selected_stage (line 87) | def write_selected_stage(self, data: core.Data):
    method read_clear_progress (line 90) | def read_clear_progress(self, data: core.Data):
    method write_clear_progress (line 93) | def write_clear_progress(self, data: core.Data):
    method read_stages (line 96) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 99) | def write_stages(self, data: core.Data):
    method read_chapter_unlock_state (line 103) | def read_chapter_unlock_state(self, data: core.Data):
    method write_chapter_unlock_state (line 106) | def write_chapter_unlock_state(self, data: core.Data):
    method serialize (line 109) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 118) | def deserialize(data: dict[str, Any]) -> Chapter:
    method __repr__ (line 125) | def __repr__(self):
    method __str__ (line 128) | def __str__(self):
  class ChaptersStars (line 132) | class ChaptersStars:
    method __init__ (line 133) | def __init__(self, chapters: list[Chapter]):
    method clear_stage (line 136) | def clear_stage(
    method unclear_stage (line 152) | def unclear_stage(self, star: int, stage: int):
    method init (line 160) | def init(total_stages: int, total_stars: int) -> ChaptersStars:
    method read_selected_stage (line 165) | def read_selected_stage(data: core.Data, total_stars: int) -> Chapters...
    method write_selected_stage (line 169) | def write_selected_stage(self, data: core.Data):
    method read_clear_progress (line 173) | def read_clear_progress(self, data: core.Data):
    method write_clear_progress (line 177) | def write_clear_progress(self, data: core.Data):
    method read_stages (line 181) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 186) | def write_stages(self, data: core.Data):
    method read_chapter_unlock_state (line 191) | def read_chapter_unlock_state(self, data: core.Data):
    method write_chapter_unlock_state (line 195) | def write_chapter_unlock_state(self, data: core.Data):
    method serialize (line 199) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 203) | def deserialize(data: list[dict[str, Any]]) -> ChaptersStars:
    method __repr__ (line 207) | def __repr__(self):
    method __str__ (line 210) | def __str__(self):
  class GauntletChapters (line 214) | class GauntletChapters:
    method __init__ (line 215) | def __init__(self, chapters: list[ChaptersStars], unknown: list[int]):
    method clear_stage (line 219) | def clear_stage(
    method unclear_stage (line 236) | def unclear_stage(self, map: int, star: int, stage: int) -> bool:
    method init (line 245) | def init() -> GauntletChapters:
    method read (line 249) | def read(data: core.Data) -> GauntletChapters:
    method write (line 272) | def write(self, data: core.Data):
    method serialize (line 297) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 304) | def deserialize(data: dict[str, Any]) -> GauntletChapters:
    method __repr__ (line 310) | def __repr__(self):
    method __str__ (line 313) | def __str__(self):
    method get_total_stars (line 316) | def get_total_stars(self, map: int) -> int:
    method get_total_stages (line 322) | def get_total_stages(self, map: int, star: int) -> int:
    method edit_gauntlets (line 329) | def edit_gauntlets(save_file: core.SaveFile):
    method edit_collab_gauntlets (line 334) | def edit_collab_gauntlets(save_file: core.SaveFile):
    method edit_behemoth_culling (line 339) | def edit_behemoth_culling(save_file: core.SaveFile):
    method edit_enigma_stages (line 344) | def edit_enigma_stages(save_file: core.SaveFile):
    method edit_chapters (line 347) | def edit_chapters(
    method unclear_rest (line 352) | def unclear_rest(self, stages: list[int], stars: int, id: int):
    method set_total_stages (line 360) | def set_total_stages(self, map: int, total_stages: int):

FILE: src/bcsfe/core/game/map/item_reward_stage.py
  class Stage (line 6) | class Stage:
    method __init__ (line 7) | def __init__(self, claimed: bool):
    method init (line 11) | def init() -> Stage:
    method read (line 15) | def read(stream: core.Data) -> Stage:
    method write (line 18) | def write(self, stream: core.Data):
    method serialize (line 21) | def serialize(self) -> bool:
    method deserialize (line 25) | def deserialize(data: bool) -> Stage:
    method __repr__ (line 28) | def __repr__(self) -> str:
    method __str__ (line 31) | def __str__(self) -> str:
  class SubChapter (line 35) | class SubChapter:
    method __init__ (line 36) | def __init__(self, stages: list[Stage]):
    method init (line 40) | def init(total_stages: int) -> SubChapter:
    method read (line 45) | def read(stream: core.Data, total_stages: int) -> SubChapter:
    method write (line 51) | def write(self, stream: core.Data):
    method serialize (line 55) | def serialize(self) -> list[bool]:
    method deserialize (line 59) | def deserialize(data: list[bool]) -> SubChapter:
    method __repr__ (line 62) | def __repr__(self) -> str:
    method __str__ (line 65) | def __str__(self) -> str:
  class SubChapterStars (line 69) | class SubChapterStars:
    method __init__ (line 70) | def __init__(self, sub_chapters: list[SubChapter]):
    method init (line 74) | def init(total_stages: int, total_stars: int) -> SubChapterStars:
    method read (line 81) | def read(
    method write (line 89) | def write(self, stream: core.Data):
    method serialize (line 93) | def serialize(self) -> list[list[bool]]:
    method deserialize (line 97) | def deserialize(data: list[list[bool]]) -> SubChapterStars:
    method __repr__ (line 102) | def __repr__(self) -> str:
    method __str__ (line 105) | def __str__(self) -> str:
  class ItemObtain (line 109) | class ItemObtain:
    method __init__ (line 110) | def __init__(self, flag: bool):
    method init (line 114) | def init() -> ItemObtain:
    method read (line 118) | def read(stream: core.Data) -> ItemObtain:
    method write (line 121) | def write(self, stream: core.Data):
    method serialize (line 124) | def serialize(self) -> bool:
    method deserialize (line 128) | def deserialize(data: bool) -> ItemObtain:
    method __repr__ (line 131) | def __repr__(self) -> str:
    method __str__ (line 134) | def __str__(self) -> str:
  class ItemObtainSet (line 138) | class ItemObtainSet:
    method __init__ (line 139) | def __init__(self, item_obtains: dict[int, ItemObtain]):
    method init (line 143) | def init() -> ItemObtainSet:
    method read (line 147) | def read(stream: core.Data) -> ItemObtainSet:
    method write (line 154) | def write(self, stream: core.Data):
    method serialize (line 160) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 169) | def deserialize(data: dict[str, Any]) -> ItemObtainSet:
    method __repr__ (line 177) | def __repr__(self) -> str:
    method __str__ (line 180) | def __str__(self) -> str:
  class ItemObtainSets (line 184) | class ItemObtainSets:
    method __init__ (line 185) | def __init__(self, item_obtain_sets: dict[int, ItemObtainSet]):
    method init (line 189) | def init() -> ItemObtainSets:
    method read (line 193) | def read(stream: core.Data) -> ItemObtainSets:
    method write (line 200) | def write(self, stream: core.Data):
    method serialize (line 206) | def serialize(self) -> dict[int, Any]:
    method deserialize (line 213) | def deserialize(data: dict[int, Any]) -> ItemObtainSets:
    method __repr__ (line 221) | def __repr__(self) -> str:
    method __str__ (line 224) | def __str__(self) -> str:
  class UnobtainedItem (line 228) | class UnobtainedItem:
    method __init__ (line 229) | def __init__(self, unobtained: bool):
    method init (line 233) | def init() -> UnobtainedItem:
    method read (line 237) | def read(stream: core.Data) -> UnobtainedItem:
    method write (line 240) | def write(self, stream: core.Data):
    method serialize (line 243) | def serialize(self) -> bool:
    method deserialize (line 247) | def deserialize(data: bool) -> UnobtainedItem:
    method __repr__ (line 250) | def __repr__(self) -> str:
    method __str__ (line 253) | def __str__(self) -> str:
  class UnobtainedItems (line 257) | class UnobtainedItems:
    method __init__ (line 258) | def __init__(self, unobtained_items: dict[int, UnobtainedItem]):
    method init (line 262) | def init() -> UnobtainedItems:
    method read (line 266) | def read(stream: core.Data) -> UnobtainedItems:
    method write (line 273) | def write(self, stream: core.Data):
    method serialize (line 279) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 288) | def deserialize(data: dict[str, Any]) -> UnobtainedItems:
    method __repr__ (line 298) | def __repr__(self) -> str:
    method __str__ (line 301) | def __str__(self) -> str:
  class ItemRewardChapters (line 305) | class ItemRewardChapters:
    method __init__ (line 306) | def __init__(self, sub_chapters: list[SubChapterStars]):
    method init (line 312) | def init(gv: core.GameVersion) -> ItemRewardChapters:
    method read (line 335) | def read(stream: core.Data, gv: core.GameVersion) -> ItemRewardChapters:
    method write (line 357) | def write(self, stream: core.Data, gv: core.GameVersion):
    method read_item_obtains (line 379) | def read_item_obtains(self, stream: core.Data):
    method write_item_obtains (line 383) | def write_item_obtains(self, stream: core.Data):
    method serialize (line 387) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 397) | def deserialize(data: dict[str, Any]) -> ItemRewardChapters:
    method __repr__ (line 412) | def __repr__(self) -> str:
    method __str__ (line 415) | def __str__(self) -> str:

FILE: src/bcsfe/core/game/map/legend_quest.py
  class Stage (line 7) | class Stage:
    method __init__ (line 8) | def __init__(self, clear_times: int):
    method init (line 12) | def init() -> Stage:
    method read (line 16) | def read(data: core.Data) -> Stage:
    method write (line 20) | def write(self, data: core.Data):
    method read_tries (line 23) | def read_tries(self, data: core.Data):
    method write_tries (line 26) | def write_tries(self, data: core.Data):
    method serialize (line 29) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 36) | def deserialize(data: dict[str, Any]) -> Stage:
    method __repr__ (line 43) | def __repr__(self):
    method __str__ (line 46) | def __str__(self):
    method clear_stage (line 49) | def clear_stage(self, clear_amount: int = 1, ensure_cleared_only: bool...
    method unclear_stage (line 57) | def unclear_stage(self):
  class Chapter (line 62) | class Chapter:
    method __init__ (line 63) | def __init__(self, selected_stage: int, total_stages: int = 0):
    method clear_stage (line 71) | def clear_stage(
    method unclear_stage (line 88) | def unclear_stage(self, index: int) -> bool:
    method init (line 94) | def init(total_stages: int) -> Chapter:
    method read_selected_stage (line 98) | def read_selected_stage(data: core.Data) -> Chapter:
    method write_selected_stage (line 102) | def write_selected_stage(self, data: core.Data):
    method read_clear_progress (line 105) | def read_clear_progress(self, data: core.Data):
    method write_clear_progress (line 108) | def write_clear_progress(self, data: core.Data):
    method read_stages (line 111) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 116) | def write_stages(self, data: core.Data):
    method read_chapter_unlock_state (line 123) | def read_chapter_unlock_state(self, data: core.Data):
    method write_chapter_unlock_state (line 126) | def write_chapter_unlock_state(self, data: core.Data):
    method serialize (line 129) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 138) | def deserialize(data: dict[str, Any]) -> Chapter:
    method __repr__ (line 147) | def __repr__(self):
    method __str__ (line 150) | def __str__(self):
  class ChaptersStars (line 154) | class ChaptersStars:
    method __init__ (line 155) | def __init__(self, chapters: list[Chapter]):
    method clear_stage (line 158) | def clear_stage(
    method unclear_stage (line 174) | def unclear_stage(self, star: int, stage: int) -> bool:
    method init (line 182) | def init(total_stages: int, total_stars: int) -> ChaptersStars:
    method read_selected_stage (line 187) | def read_selected_stage(data: core.Data, total_stars: int) -> Chapters...
    method write_selected_stage (line 191) | def write_selected_stage(self, data: core.Data):
    method read_clear_progress (line 195) | def read_clear_progress(self, data: core.Data):
    method write_clear_progress (line 199) | def write_clear_progress(self, data: core.Data):
    method read_stages (line 203) | def read_stages(self, data: core.Data, total_stages: int):
    method write_stages (line 212) | def write_stages(self, data: core.Data):
    method read_chapter_unlock_state (line 221) | def read_chapter_unlock_state(self, data: core.Data):
    method write_chapter_unlock_state (line 225) | def write_chapter_unlock_state(self, data: core.Data):
    method serialize (line 229) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 233) | def deserialize(data: list[dict[str, Any]]) -> ChaptersStars:
    method __repr__ (line 237) | def __repr__(self):
    method __str__ (line 240) | def __str__(self):
  class LegendQuestChapters (line 244) | class LegendQuestChapters:
    method __init__ (line 245) | def __init__(
    method clear_stage (line 252) | def clear_stage(
    method unclear_stage (line 269) | def unclear_stage(self, map: int, star: int, stage: int) -> bool:
    method init (line 278) | def init() -> LegendQuestChapters:
    method read (line 282) | def read(data: core.Data) -> LegendQuestChapters:
    method write (line 306) | def write(self, data: core.Data):
    method serialize (line 335) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 343) | def deserialize(data: dict[str, Any]) -> LegendQuestChapters:
    method __repr__ (line 351) | def __repr__(self):
    method __str__ (line 354) | def __str__(self):
    method get_total_stars (line 357) | def get_total_stars(self, map: int) -> int:
    method get_total_stages (line 363) | def get_total_stages(self, map: int, star: int) -> int:
    method edit_legend_quest (line 370) | def edit_legend_quest(save_file: core.SaveFile):
    method edit_chapters (line 374) | def edit_chapters(
    method unclear_rest (line 379) | def unclear_rest(self, stages: list[int], stars: int, id: int):
    method set_total_stages (line 387) | def set_total_stages(self, map: int, total_stages: int):

FILE: src/bcsfe/core/game/map/map_names.py
  class MapNames (line 6) | class MapNames:
    method __init__ (line 7) | def __init__(
    method get_map_names_in_game (line 25) | def get_map_names_in_game(
    method get_map_names (line 52) | def get_map_names(self) -> dict[int, str | None] | None:
    method get_code_from_id (line 78) | def get_code_from_id(id: int) -> str | None:
    method from_id (line 108) | def from_id(id: int, save_file: core.SaveFile) -> MapNames | None:

FILE: src/bcsfe/core/game/map/map_option.py
  class MapOptionLine (line 6) | class MapOptionLine:
    method __init__ (line 7) | def __init__(
    method from_line (line 36) | def from_line(line: core.Row) -> MapOptionLine:
  class MapOption (line 53) | class MapOption:
    method __init__ (line 54) | def __init__(self, maps: dict[int, MapOptionLine]):
    method from_csv (line 58) | def from_csv(csv: core.CSV) -> MapOption:
    method from_save (line 68) | def from_save(save_file: core.SaveFile) -> MapOption | None:
    method get_map (line 78) | def get_map(self, map_id: int) -> MapOptionLine | None:

FILE: src/bcsfe/core/game/map/map_reset.py
  class MapResetData (line 5) | class MapResetData:
    method __init__ (line 6) | def __init__(
    method init (line 19) | def init() -> MapResetData:
    method read (line 28) | def read(stream: core.Data) -> MapResetData:
    method write (line 40) | def write(self, stream: core.Data):
    method serialize (line 46) | def serialize(self) -> dict[str, float]:
    method deserialize (line 55) | def deserialize(data: dict[str, float]) -> MapResetData:
    method __str__ (line 63) | def __str__(self) -> str:
    method __repr__ (line 66) | def __repr__(self) -> str:
  class MapResets (line 70) | class MapResets:
    method __init__ (line 71) | def __init__(self, data: dict[int, list[MapResetData]]):
    method init (line 75) | def init() -> MapResets:
    method read (line 79) | def read(stream: core.Data) -> MapResets:
    method write (line 89) | def write(self, stream: core.Data):
    method serialize (line 97) | def serialize(self) -> dict[int, list[dict[str, float]]]:
    method deserialize (line 104) | def deserialize(data: dict[int, list[dict[str, float]]]) -> MapResets:
    method __str__ (line 112) | def __str__(self) -> str:
    method __repr__ (line 115) | def __repr__(self) -> str:

FILE: src/bcsfe/core/game/map/outbreaks.py
  class Outbreak (line 7) | class Outbreak:
    method __init__ (line 8) | def __init__(self, cleared: bool):
    method init (line 12) | def init() -> Outbreak:
    method read (line 16) | def read(stream: core.Data) -> Outbreak:
    method write (line 20) | def write(self, stream: core.Data):
    method serialize (line 23) | def serialize(self) -> bool:
    method deserialize (line 27) | def deserialize(data: bool) -> Outbreak:
    method __repr__ (line 30) | def __repr__(self) -> str:
    method __str__ (line 33) | def __str__(self) -> str:
  class Chapter (line 37) | class Chapter:
    method __init__ (line 38) | def __init__(self, id: int, outbreaks: dict[int, Outbreak]):
    method get_true_id (line 42) | def get_true_id(self) -> int:
    method init (line 48) | def init(id: int) -> Chapter:
    method read (line 52) | def read(stream: core.Data, id: int) -> Chapter:
    method write (line 62) | def write(self, stream: core.Data):
    method serialize (line 68) | def serialize(self) -> dict[int, Any]:
    method deserialize (line 75) | def deserialize(data: dict[int, Any], id: int) -> Chapter:
    method __repr__ (line 84) | def __repr__(self) -> str:
    method __str__ (line 87) | def __str__(self) -> str:
  class Outbreaks (line 91) | class Outbreaks:
    method __init__ (line 92) | def __init__(self, chapters: dict[int, Chapter]):
    method init (line 98) | def init() -> Outbreaks:
    method read_chapters (line 102) | def read_chapters(stream: core.Data) -> Outbreaks:
    method write_chapters (line 112) | def write_chapters(self, stream: core.Data):
    method read_2 (line 118) | def read_2(self, stream: core.Data):
    method write_2 (line 121) | def write_2(self, stream: core.Data):
    method read_current_outbreaks (line 124) | def read_current_outbreaks(self, stream: core.Data, gv: core.GameVersi...
    method write_current_outbreaks (line 143) | def write_current_outbreaks(self, stream: core.Data, gv: core.GameVers...
    method serialize (line 151) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 165) | def deserialize(data: dict[str, Any]) -> Outbreaks:
    method __repr__ (line 182) | def __repr__(self) -> str:
    method __str__ (line 185) | def __str__(self) -> str:
    method get_chapter_from_true_id (line 188) | def get_chapter_from_true_id(self, true_id: int) -> Chapter | None:
    method get_current_chapter_from_true_id (line 193) | def get_current_chapter_from_true_id(self, true_id: int) -> Chapter | ...
    method clear_outbreak (line 198) | def clear_outbreak(self, chapter_id: int, stage_id: int, clear: bool):
    method edit_outbreaks (line 212) | def edit_outbreaks(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/map/story.py
  class Stage (line 7) | class Stage:
    method __init__ (line 8) | def __init__(self, clear_times: int):
    method init (line 14) | def init() -> Stage:
    method read_clear_times (line 18) | def read_clear_times(stream: core.Data) -> Stage:
    method read_treasure (line 21) | def read_treasure(self, stream: core.Data):
    method read_itf_timed_score (line 24) | def read_itf_timed_score(self, stream: core.Data):
    method write_clear_times (line 27) | def write_clear_times(self, stream: core.Data):
    method write_treasure (line 30) | def write_treasure(self, stream: core.Data):
    method write_itf_timed_score (line 33) | def write_itf_timed_score(self, stream: core.Data):
    method serialize (line 36) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 44) | def deserialize(data: dict[str, Any]) -> Stage:
    method __repr__ (line 50) | def __repr__(self):
    method __str__ (line 53) | def __str__(self):
    method clear_stage (line 56) | def clear_stage(self, clear_amount: int = 1):
    method unclear_stage (line 59) | def unclear_stage(self):
    method is_cleared (line 62) | def is_cleared(self) -> bool:
    method set_treasure (line 65) | def set_treasure(self, treasure: int):
  class Chapter (line 69) | class Chapter:
    method __init__ (line 70) | def __init__(self, selected_stage: int):
    method clear_stage (line 80) | def clear_stage(
    method set_treasure (line 92) | def set_treasure(self, stage_id: int, treasure: int):
    method is_stage_clear (line 95) | def is_stage_clear(self, stage_id: int) -> bool:
    method init (line 99) | def init() -> Chapter:
    method get_treasure_stages (line 102) | def get_treasure_stages(self) -> list[Stage]:
    method get_valid_treasure_stages (line 105) | def get_valid_treasure_stages(self) -> list[Stage]:
    method read_selected_stage (line 109) | def read_selected_stage(stream: core.Data) -> Chapter:
    method read_progress (line 112) | def read_progress(self, stream: core.Data):
    method read_clear_times (line 115) | def read_clear_times(self, stream: core.Data):
    method read_treasure (line 119) | def read_treasure(self, stream: core.Data):
    method read_time_until_treasure_chance (line 123) | def read_time_until_treasure_chance(self, stream: core.Data):
    method read_treasure_chance_duration (line 126) | def read_treasure_chance_duration(self, stream: core.Data):
    method read_treasure_chance_value (line 129) | def read_treasure_chance_value(self, stream: core.Data):
    method read_treasure_chance_stage_id (line 132) | def read_treasure_chance_stage_id(self, stream: core.Data):
    method read_treasure_festival_type (line 135) | def read_treasure_festival_type(self, stream: core.Data):
    method read_itf_timed_scores (line 138) | def read_itf_timed_scores(self, stream: core.Data):
    method write_selected_stage (line 142) | def write_selected_stage(self, stream: core.Data):
    method write_progress (line 145) | def write_progress(self, stream: core.Data):
    method write_clear_times (line 148) | def write_clear_times(self, stream: core.Data):
    method write_treasure (line 152) | def write_treasure(self, stream: core.Data):
    method write_time_until_treasure_chance (line 156) | def write_time_until_treasure_chance(self, stream: core.Data):
    method write_treasure_chance_duration (line 159) | def write_treasure_chance_duration(self, stream: core.Data):
    method write_treasure_chance_value (line 162) | def write_treasure_chance_value(self, stream: core.Data):
    method write_treasure_chance_stage_id (line 165) | def write_treasure_chance_stage_id(self, stream: core.Data):
    method write_treasure_festival_type (line 168) | def write_treasure_festival_type(self, stream: core.Data):
    method write_itf_timed_scores (line 171) | def write_itf_timed_scores(self, stream: core.Data):
    method serialize (line 175) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 188) | def deserialize(data: dict[str, Any]) -> Chapter:
    method __repr__ (line 199) | def __repr__(self):
    method __str__ (line 202) | def __str__(self):
    method apply_progress (line 205) | def apply_progress(self, progress: int, clear_times: list[int] | None ...
    method clear_chapter (line 216) | def clear_chapter(self):
  class StoryChapters (line 220) | class StoryChapters:
    method __init__ (line 221) | def __init__(self, chapters: list[Chapter]):
    method get_real_chapters (line 224) | def get_real_chapters(self) -> list[Chapter]:
    method clear_stage (line 232) | def clear_stage(
    method set_treasure (line 244) | def set_treasure(self, chapter: int, stage: int, treasure: int):
    method is_stage_clear (line 247) | def is_stage_clear(self, chapter: int, stage: int) -> bool:
    method init (line 251) | def init() -> StoryChapters:
    method read (line 256) | def read(stream: core.Data) -> StoryChapters:
    method read_treasure_festival (line 270) | def read_treasure_festival(self, stream: core.Data):
    method write (line 282) | def write(self, stream: core.Data):
    method write_treasure_festival (line 292) | def write_treasure_festival(self, stream: core.Data):
    method read_itf_timed_scores (line 304) | def read_itf_timed_scores(self, stream: core.Data):
    method write_itf_timed_scores (line 320) | def write_itf_timed_scores(self, stream: core.Data):
    method serialize (line 325) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 330) | def deserialize(data: list[dict[str, Any]]) -> StoryChapters:
    method __repr__ (line 334) | def __repr__(self):
    method __str__ (line 337) | def __str__(self):
    method clear_tutorial (line 341) | def clear_tutorial(save_file: core.SaveFile):
    method get_chapter_names (line 357) | def get_chapter_names(
    method select_story_chapters (line 382) | def select_story_chapters(
    method get_selected_chapter_progress (line 397) | def get_selected_chapter_progress(max_stages: int = 48) -> int | None:
    method edit_chapter_progress (line 407) | def edit_chapter_progress(
    method convert_stage_id (line 444) | def convert_stage_id(index: int) -> int:
    method ask_clear_count (line 453) | def ask_clear_count() -> int | None:
    method ask_if_individual_clear_counts (line 462) | def ask_if_individual_clear_counts() -> bool | None:
    method edit_stage_clear_count (line 473) | def edit_stage_clear_count(
    method clear_previous_chapters (line 483) | def clear_previous_chapters(self, chapter_id: int):
    method print_current_chapter (line 525) | def print_current_chapter(save_file: core.SaveFile, chapter_id: int):
    method print_current_treasure_group (line 533) | def print_current_treasure_group(
    method clear_story (line 549) | def clear_story(save_file: core.SaveFile):
    method edit_chapters (line 555) | def edit_chapters(self, save_file: core.SaveFile):
    method ask_treasure_level (line 671) | def ask_treasure_level(save_file: core.SaveFile) -> int | None:
    method get_per_chapter (line 704) | def get_per_chapter(chapters: list[int]) -> int | None:
    method edit_treasures_whole_chapters (line 718) | def edit_treasures_whole_chapters(save_file: core.SaveFile, chapters: ...
    method get_chapter_type_from_index (line 742) | def get_chapter_type_from_index(index: int) -> int:
    method select_stages (line 750) | def select_stages(save_file: core.SaveFile, chapter_id: int) -> list[i...
    method edit_treasures_individual_stages (line 786) | def edit_treasures_individual_stages(save_file: core.SaveFile, chapter...
    method edit_treasures_groups (line 817) | def edit_treasures_groups(save_file: core.SaveFile, chapters: list[int]):
    method edit_treasures (line 872) | def edit_treasures(save_file: core.SaveFile):
    method edit_itf_timed_scores (line 894) | def edit_itf_timed_scores(save_file: core.SaveFile):
    method edit_itf_timed_scores_whole_chapters (line 922) | def edit_itf_timed_scores_whole_chapters(
    method print_current_stage (line 955) | def print_current_stage(save_file: core.SaveFile, chapter_id: int, sta...
    method edit_itf_timed_scores_individual_stages (line 971) | def edit_itf_timed_scores_individual_stages(
  class StageNames (line 1045) | class StageNames:
    method __init__ (line 1046) | def __init__(self, save_file: core.SaveFile, chapter: str, max_stages:...
    method get_file_name (line 1052) | def get_file_name(self) -> str:
    method get_stage_names (line 1059) | def get_stage_names(self) -> list[str] | None:
    method get_stage_name (line 1079) | def get_stage_name(self, stage_id: int) -> str | None:
  class TreasureText (line 1085) | class TreasureText:
    method __init__ (line 1086) | def __init__(self, save_file: core.SaveFile):
    method get_tt_file_name (line 1090) | def get_tt_file_name(self) -> str:
    method get_treasure_text (line 1093) | def get_treasure_text(self) -> list[str] | None:
  class TreasureGroupData (line 1109) | class TreasureGroupData:
    method __init__ (line 1110) | def __init__(self, save_file: core.SaveFile, chapter_type: int):
    method get_tgd_file_name (line 1115) | def get_tgd_file_name(self) -> str:
    method get_treasure_group_data (line 1124) | def get_treasure_group_data(self) -> list[list[int]] | None:
  class TreasureGroupNames (line 1139) | class TreasureGroupNames:
    method __init__ (line 1140) | def __init__(self, save_file: core.SaveFile, chapter_type: int):
    method get_tgn_file_name (line 1145) | def get_tgn_file_name(self) -> str:
    method get_treasure_group_names (line 1155) | def get_treasure_group_names(self) -> list[str] | None:

FILE: src/bcsfe/core/game/map/timed_score.py
  class Stage (line 5) | class Stage:
    method __init__ (line 6) | def __init__(self, score: int):
    method init (line 10) | def init() -> Stage:
    method read (line 14) | def read(stream: core.Data) -> Stage:
    method write (line 17) | def write(self, stream: core.Data):
    method serialize (line 20) | def serialize(self) -> int:
    method deserialize (line 24) | def deserialize(data: int) -> Stage:
    method __repr__ (line 27) | def __repr__(self) -> str:
    method __str__ (line 30) | def __str__(self) -> str:
  class SubChapter (line 34) | class SubChapter:
    method __init__ (line 35) | def __init__(self, stages: list[Stage]):
    method init (line 39) | def init(total_stages: int) -> SubChapter:
    method read (line 43) | def read(stream: core.Data, total_stages: int) -> SubChapter:
    method write (line 49) | def write(self, stream: core.Data):
    method serialize (line 53) | def serialize(self) -> list[int]:
    method deserialize (line 57) | def deserialize(data: list[int]) -> SubChapter:
    method __repr__ (line 60) | def __repr__(self) -> str:
    method __str__ (line 63) | def __str__(self) -> str:
  class SubChapterStars (line 67) | class SubChapterStars:
    method __init__ (line 68) | def __init__(self, sub_chapters: list[SubChapter]):
    method init (line 72) | def init(total_stages: int, total_stars: int) -> SubChapterStars:
    method read (line 78) | def read(
    method write (line 88) | def write(self, stream: core.Data):
    method serialize (line 92) | def serialize(self) -> list[list[int]]:
    method deserialize (line 96) | def deserialize(data: list[list[int]]) -> SubChapterStars:
    method __repr__ (line 101) | def __repr__(self) -> str:
    method __str__ (line 104) | def __str__(self) -> str:
  class TimedScoreChapters (line 108) | class TimedScoreChapters:
    method __init__ (line 109) | def __init__(self, sub_chapters: list[SubChapterStars]):
    method init (line 113) | def init(gv: core.GameVersion) -> TimedScoreChapters:
    method read (line 136) | def read(stream: core.Data, gv: core.GameVersion) -> TimedScoreChapters:
    method write (line 158) | def write(self, stream: core.Data, gv: core.GameVersion):
    method serialize (line 180) | def serialize(self) -> list[list[list[int]]]:
    method deserialize (line 184) | def deserialize(data: list[list[list[int]]]) -> TimedScoreChapters:
    method __repr__ (line 189) | def __repr__(self) -> str:
    method __str__ (line 192) | def __str__(self) -> str:

FILE: src/bcsfe/core/game/map/tower.py
  class TowerChapters (line 6) | class TowerChapters:
    method __init__ (line 7) | def __init__(self, chapters: core.Chapters):
    method init (line 12) | def init() -> TowerChapters:
    method read (line 16) | def read(data: core.Data) -> TowerChapters:
    method write (line 20) | def write(self, data: core.Data):
    method read_item_obtain_states (line 23) | def read_item_obtain_states(self, data: core.Data):
    method write_item_obtain_states (line 30) | def write_item_obtain_states(self, data: core.Data):
    method serialize (line 39) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 46) | def deserialize(data: dict[str, Any]) -> TowerChapters:
    method __repr__ (line 53) | def __repr__(self):
    method __str__ (line 56) | def __str__(self):
    method get_total_stars (line 59) | def get_total_stars(self, chapter_id: int) -> int:
    method get_total_stages (line 62) | def get_total_stages(self, chapter_id: int, star: int) -> int:
    method edit_towers (line 66) | def edit_towers(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/map/uncanny.py
  class UncannyChapters (line 7) | class UncannyChapters:
    method __init__ (line 8) | def __init__(self, chapters: core.Chapters, unknown: list[int]):
    method init (line 13) | def init() -> UncannyChapters:
    method read (line 17) | def read(data: core.Data) -> UncannyChapters:
    method write (line 22) | def write(self, data: core.Data):
    method serialize (line 26) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 33) | def deserialize(data: dict[str, Any]) -> UncannyChapters:
    method __repr__ (line 39) | def __repr__(self):
    method __str__ (line 42) | def __str__(self):
    method edit_uncanny (line 46) | def edit_uncanny(save_file: core.SaveFile):
    method edit_catamin_stages (line 51) | def edit_catamin_stages(save_file: core.SaveFile):

FILE: src/bcsfe/core/game/map/zero_legends.py
  class Stage (line 7) | class Stage:
    method __init__ (line 8) | def __init__(self, clear_times: int):
    method init (line 12) | def init() -> Stage:
    method read (line 16) | def read(data: core.Data) -> Stage:
    method write (line 20) | def write(self, data: core.Data):
    method serialize (line 23) | def serialize(self) -> int:
    method deserialize (line 27) | def deserialize(data: int) -> Stage:
    method __repr__ (line 32) | def __repr__(self):
    method __str__ (line 35) | def __str__(self):
    method clear_stage (line 38) | def clear_stage(self, clear_amount: int = 1, ensure_cleared_only: bool...
    method unclear_stage (line 44) | def unclear_stage(self):
  class Chapter (line 48) | class Chapter:
    method __init__ (line 49) | def __init__(
    method clear_stage (line 63) | def clear_stage(
    method unclear_stage (line 80) | def unclear_stage(self, index: int) -> bool:
    method init (line 86) | def init() -> Chapter:
    method read (line 90) | def read(data: core.Data) -> Chapter:
    method write (line 103) | def write(self, data: core.Data):
    method serialize (line 111) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 120) | def deserialize(data: dict[str, Any]) -> Chapter:
    method __repr__ (line 128) | def __repr__(self):
    method __str__ (line 131) | def __str__(self):
  class ChaptersStars (line 135) | class ChaptersStars:
    method __init__ (line 136) | def __init__(self, unknown: int, chapters: list[Chapter]):
    method clear_stage (line 140) | def clear_stage(
    method unclear_stage (line 156) | def unclear_stage(self, star: int, stage: int) -> bool:
    method init (line 164) | def init() -> ChaptersStars:
    method read (line 168) | def read(data: core.Data) -> ChaptersStars:
    method write (line 177) | def write(self, data: core.Data):
    method serialize (line 183) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 190) | def deserialize(data: dict[str, Any]) -> ChaptersStars:
    method __repr__ (line 196) | def __repr__(self):
    method __str__ (line 199) | def __str__(self):
  class ZeroLegendsChapters (line 203) | class ZeroLegendsChapters:
    method __init__ (line 204) | def __init__(self, chapters: list[ChaptersStars]):
    method clear_stage (line 207) | def clear_stage(
    method unclear_stage (line 225) | def unclear_stage(self, map: int, star: int, stage: int) -> bool:
    method init (line 235) | def init() -> ZeroLegendsChapters:
    method read (line 239) | def read(data: core.Data) -> ZeroLegendsChapters:
    method write (line 246) | def write(self, data: core.Data):
    method serialize (line 251) | def serialize(self) -> list[dict[str, Any]]:
    method deserialize (line 255) | def deserialize(data: list[dict[str, Any]]) -> ZeroLegendsChapters:
    method __repr__ (line 260) | def __repr__(self):
    method __str__ (line 263) | def __str__(self):
    method get_total_stars (line 266) | def get_total_stars(self, chapter_id: int) -> int:
    method get_total_stages (line 269) | def get_total_stages(self, chapter_id: int, star: int) -> int:
    method create (line 272) | def create(self, chapter_id: int):
    method edit_zero_legends (line 283) | def edit_zero_legends(save_file: core.SaveFile):
    method edit_catclaw_championships (line 289) | def edit_catclaw_championships(save_file: core.SaveFile):
    method edit_chapters (line 293) | def edit_chapters(
    method unclear_rest (line 304) | def unclear_rest(self, stages: list[int], stars: int, id: int):
    method set_total_stages (line 312) | def set_total_stages(self, map: int, total_stages: int):

FILE: src/bcsfe/core/game_version.py
  class GameVersion (line 6) | class GameVersion:
    method __init__ (line 9) | def __init__(self, game_version: int):
    method to_string (line 17) | def to_string(self) -> str:
    method get_parts_zfill (line 29) | def get_parts_zfill(self) -> list[str]:
    method get_parts (line 37) | def get_parts(self) -> list[int]:
    method format (line 45) | def format(self) -> str:
    method __str__ (line 57) | def __str__(self) -> str:
    method __repr__ (line 65) | def __repr__(self) -> str:
    method read (line 74) | def read(data: core.Data) -> GameVersion:
    method write (line 85) | def write(self, data: core.Data):
    method serialize (line 93) | def serialize(self) -> dict[str, Any]:
    method deserialize (line 102) | def deserialize(game_version: dict[str, Any]) -> GameVersion:
    method from_string (line 114) | def from_string(game_version: str) -> GameVersion:
    method __eq__ (line 131) | def __eq__(self, other: Any) -> bool:
    method __ne__ (line 151) | def __ne__(self, other: Any) -> bool:
    method __lt__ (line 162) | def __lt__(self, other: Any) -> bool:
    method __le__ (line 182) | def __le__(self, other: Any) -> bool:
    method __gt__ (line 193) | def __gt__(self, other: Any) -> bool:
    method __ge__ (line 204) | def __ge__(self, other: Any) -> bool:
    method __add__ (line 215) | def __add__(self, other: Any) -> GameVersion:
    method __sub__ (line 235) | def __sub__(self, other: Any) -> GameVersion:

FILE: src/bcsfe/core/io/adb_handler.py
  class DeviceIDNotSet (line 7) | class DeviceIDNotSet(Exception):
  class AdbNotInstalled (line 11) | class AdbNotInstalled(Exception):
    method __init__ (line 12) | def __init__(self, result: core.CommandResult):
  class AdbHandler (line 16) | class AdbHandler(io.root_handler.RootHandler):
    method __init__ (line 17) | def __init__(self, root: bool = True):
    method display_no_adb_error (line 28) | def display_no_adb_error(e: AdbNotInstalled):
    method check_adb_installed (line 35) | def check_adb_installed(self, path: core.Path):
    method adb_root_success (line 40) | def adb_root_success(self) -> bool:
    method start_server (line 49) | def start_server(self) -> core.CommandResult:
    method kill_server (line 52) | def kill_server(self) -> core.CommandResult:
    method root (line 55) | def root(self) -> core.CommandResult:
    method get_connected_devices (line 58) | def get_connected_devices(self) -> list[str]:
    method set_device (line 63) | def set_device(self, device_id: str):
    method get_device (line 68) | def get_device(self) -> str:
    method get_device_name (line 73) | def get_device_name(self) -> str:
    method run_shell (line 76) | def run_shell(self, command: str) -> core.CommandResult:
    method run_root_shell (line 79) | def run_root_shell(self, command: str) -> core.CommandResult:
    method adb_pull_file (line 82) | def adb_pull_file(
    method pull_file (line 89) | def pull_file(
    method adb_push_file (line 110) | def adb_push_file(
    method push_file (line 117) | def push_file(
    method stat_file (line 139) | def stat_file(self, device_path: core.Path) -> core.CommandResult:
    method does_file_exist (line 142) | def does_file_exist(self, device_path: core.Path) -> bool:
    method get_battlecats_packages (line 145) | def get_battlecats_packages(self) -> list[str]:
    method save_battlecats_save (line 158) | def save_battlecats_save(self, local_path: core.Path) -> core.CommandR...
    method load_battlecats_save (line 162) | def load_battlecats_save(self, local_path: core.Path) -> core.CommandR...
    method close_game (line 165) | def close_game(self) -> core.CommandResult:
    method run_game (line 168) | def run_game(self) -> core.CommandResult:
    method select_device (line 171) | def select_device(self) -> bool:

FILE: src/bcsfe/core/io/bc_csv.py
  class DelimeterType (line 9) | class DelimeterType(enum.Enum):
  class Delimeter (line 15) | class Delimeter:
    method __init__ (line 16) | def __init__(self, de: DelimeterType | str):
    method from_country_code_res (line 23) | def from_country_code_res(cc: core.CountryCode) -> Delimeter:
    method __str__ (line 28) | def __str__(self) -> str:
  class Cell (line 32) | class Cell:
    method __init__ (line 33) | def __init__(self, dt: core.Data):
    method to_str (line 36) | def to_str(self) -> str:
    method to_int (line 39) | def to_int(self) -> int:
    method to_bool (line 42) | def to_bool(self) -> bool:
    method __repr__ (line 45) | def __repr__(self) -> str:
    method __str__ (line 48) | def __str__(self) -> str:
  class Row (line 52) | class Row:
    method __init__ (line 53) | def __init__(self, cells: list[Cell]):
    method __getitem__ (line 58) | def __getitem__(self, index: int) -> Cell: ...
    method __getitem__ (line 61) | def __getitem__(self, index: slice) -> Row: ...
    method __getitem__ (line 63) | def __getitem__(self, index: int | slice) -> Cell | Row:
    method __len__ (line 74) | def __len__(self) -> int:
    method from_list (line 78) | def from_list(dt: list[core.Data]) -> Row:
    method __repr__ (line 84) | def __repr__(self) -> str:
    method __str__ (line 87) | def __str__(self) -> str:
    method __iter__ (line 90) | def __iter__(self):
    method __next__ (line 94) | def __next__(self):
    method next (line 101) | def next(self):
    method next_opt (line 104) | def next_opt(self) -> Cell | None:
    method done (line 109) | def done(self):
    method next_int (line 112) | def next_int(self) -> int:
    method next_str (line 115) | def next_str(self) -> str:
    method next_bool (line 118) | def next_bool(self) -> bool:
    method next_int_opt (line 121) | def next_int_opt(self) -> int | None:
    method next_str_opt (line 127) | def next_str_opt(self) -> str | None:
    method next_bool_opt (line 133) | def next_bool_opt(self) -> bool | None:
    method to_str_list (line 139) | def to_str_list(self) -> list[str]:
    method to_int_list (line 142) | def to_int_list(self) -> list[int]:
  class CSV (line 146) | class CSV:
    method __init__ (line 147) | def __init__(
    method parse (line 169) | def parse(self):
    method get_row (line 189) | def get_row(self, index: int) -> Row:
    method __getitem__ (line 195) | def __getitem__(self, index: int) -> Row:
    method __len__ (line 198) | def __len__(self) -> int:
    method from_file (line 202) | def from_file(
    method add_line (line 207) | def add_line(self, line: list[Any] | Any):
    method set_line (line 215) | def set_line(self, index: int, line: list[Any]):
    method to_data (line 224) | def to_data(self) -> core.Data:
    method read_line (line 234) | def read_line(self) -> Row | None:
    method reset_index (line 243) | def reset_index(self):
    method has_line (line 246) | def has_line(self) -> bool:
    method __iter__ (line 249) | def __iter__(self):
    method __next__ (line 252) | def __next__(self) -> Row:
    method extend (line 258) | def extend(self, length: int, sub_length: int = 0):

FILE: src/bcsfe/core/io/command.py
  class CommandResult (line 6) | class CommandResult:
    method __init__ (line 7) | def __init__(self, result: str, exit_code: int):
    method __str__ (line 11) | def __str__(self) -> str:
    method __repr__ (line 14) | def __repr__(self) -> str:
    method success (line 18) | def success(self) -> bool:
    method create_success (line 22) | def create_success(result: str = "") -> CommandResult:
    method create_failure (line 26) | def create_failure(result: str = "") -> CommandResult:
  class Command (line 30) | class Command:
    method __init__ (line 31) | def __init__(self, command: str, display_output: bool = True):
    method run (line 35) | def run(self, inputData: str = "\n") -> CommandResult:
    method run_in_thread (line 48) | def run_in_thread(self, inputData: str = "\n") -> None:

FILE: src/bcsfe/core/io/config.py
  class ConfigKey (line 9) | class ConfigKey(enum.Enum):
  class Config (line 35) | class Config:
    method __init__ (line 36) | def __init__(self, path: core.Path | None, print_yaml_err: bool = True):
    method get_config_path (line 50) | def get_config_path() -> core.Path:
    method __getitem__ (line 53) | def __getitem__(self, key: ConfigKey) -> Any:
    method __setitem__ (line 56) | def __setitem__(self, key: ConfigKey, value: Any) -> None:
    method __contains__ (line 59) | def __contains__(self, key: ConfigKey) -> bool:
    method get_defaults (line 63) | def get_defaults() -> dict[ConfigKey, Any]:
    method get_default (line 91) | def get_default(self, key: ConfigKey) -> Any:
    method set_default (line 95) | def set_default(self, key: ConfigKey):
    method initialize_config (line 101) | def initialize_config(self):
    method save (line 109) | def save(self):
    method get (line 114) | def get(self, key: ConfigKey) -> Any:
    method get_game_data_repo (line 120) | def get_game_data_repo(self, fix_old_repo: bool = True) -> str:
    method get_str (line 130) | def get_str(self, key: ConfigKey) -> str:
    method get_bool (line 136) | def get_bool(self, key: ConfigKey) -> bool:
    method get_int (line 142) | def get_int(self, key: ConfigKey) -> int:
    method reset (line 148) | def reset(self):
    method set (line 153) | def set(self, key: ConfigKey, value: Any):
    method get_bool_text (line 157) | def get_bool_text(self, value: bool) -> str:
    method get_full_input_localized (line 162) | def get_full_input_localized(
    method edit_bool (line 176) | def edit_bool(self, key: ConfigKey):
    method get_desc_key (line 207) | def get_desc_key(key: ConfigKey) -> str:
    method edit_int (line 210) | def edit_int(self, key: ConfigKey):
    method edit_game_data_repo (line 224) | def edit_game_data_repo(self):
    method edit_str (line 255) | def edit_str(self, key: ConfigKey):
    method edit_locale (line 275) | def edit_locale(self):
    method edit_theme (line 361) | def edit_theme(self):
    method edit_config (line 448) | def edit_config(_: Any = None):

FILE: src/bcsfe/core/io/data.py
  class PaddingType (line 12) | class PaddingType(enum.Enum):
  class Data (line 18) | class Data:
    method __init__ (line 19) | def __init__(
    method __bytes__ (line 45) | def __bytes__(self) -> bytes:
    method __buffer__ (line 48) | def __buffer__(self, flags: int, /) -> memoryview:
    method from_hex (line 52) | def from_hex(hex: str) -> Data:
    method enable_buffer (line 55) | def enable_buffer(self):
    method end_buffer (line 59) | def end_buffer(self):
    method set_endiness (line 64) | def set_endiness(self, endiness: Literal["<", ">"]):
    method set_little_endiness (line 67) | def set_little_endiness(self):
    method set_big_endiness (line 70) | def set_big_endiness(self):
    method is_empty (line 73) | def is_empty(self) -> bool:
    method to_file (line 76) | def to_file(self, path: core.Path):
    method copy (line 80) | def copy(self) -> Data:
    method from_file (line 84) | def from_file(path: core.Path) -> Data:
    method set_pos (line 88) | def set_pos(self, pos: int):
    method reset_pos (line 93) | def reset_pos(self):
    method clear (line 96) | def clear(self):
    method get_pos (line 100) | def get_pos(self) -> int:
    method to_hex (line 103) | def to_hex(self) -> str:
    method __len__ (line 106) | def __len__(self) -> int:
    method __add__ (line 109) | def __add__(self, other: Data) -> Data:
    method __getitem__ (line 113) | def __getitem__(self, key: int) -> int:
    method __getitem__ (line 117) | def __getitem__(self, key: slice) -> Data:
    method __getitem__ (line 120) | def __getitem__(self, key: int | slice) -> int | Data:
    method __eq__ (line 128) | def __eq__(self, other: Any) -> bool:
    method get_bytes (line 134) | def get_bytes(self) -> bytes:
    method read_bytes (line 137) | def read_bytes(self, length: int) -> bytes:
    method read_to_end (line 142) | def read_to_end(self, remaining_data: int = 0) -> bytes:
    method read_int (line 146) | def read_int(self) -> int:
    method read_variable_length_int (line 150) | def read_variable_length_int(self) -> int:
    method write_variable_length_int (line 160) | def write_variable_length_int(self, value: int):
    method read_int_list (line 173) | def read_int_list(self, length: int | None = None) -> list[int]:
    method read_bool_list (line 181) | def read_bool_list(self, length: int | None = None) -> list[bool]:
    method read_string_list (line 189) | def read_string_list(self, length: int | None = None) -> list[str]:
    method read_byte_list (line 197) | def read_byte_list(self, length: int | None = None) -> list[int]:
    method read_short_list (line 205) | def read_short_list(self, length: int | None = None) -> list[int]:
    method read_uint (line 213) | def read_uint(self) -> int:
    method read_short (line 217) | def read_short(self) -> int:
    method read_ushort (line 221) | def read_ushort(self) -> int:
    method read_byte (line 225) | def read_byte(self) -> int:
    method read_ubyte (line 229) | def read_ubyte(self) -> int:
    method read_float (line 233) | def read_float(self) -> float:
    method read_double (line 237) | def read_double(self) -> float:
    method read_string (line 241) | def read_string(self, length: int | None = None) -> str:
    method read_utf8_string_by_char_length (line 247) | def read_utf8_string_by_char_length(self, length: int | None = None) -...
    method read_long (line 265) | def read_long(self) -> int:
    method read_ulong (line 269) | def read_ulong(self) -> int:
    method read_date (line 273) | def read_date(self):
    method write_date (line 282) | def write_date(self, date: datetime.datetime):
    method write_bytes (line 290) | def write_bytes(self, data: bytes):
    method write_int (line 297) | def write_int(self, value: int):
    method write_uint (line 301) | def write_uint(self, value: int):
    method write_short (line 305) | def write_short(self, value: int):
    method write_ushort (line 309) | def write_ushort(self, value: int):
    method write_byte (line 313) | def write_byte(self, value: int):
    method write_ubyte (line 317) | def write_ubyte(self, value: int):
    method write_float (line 321) | def write_float(self, value: float):
    method write_double (line 324) | def write_double(self, value: float):
    method write_string (line 327) | def write_string(self, value: str, write_length: bool = True):
    method write_long (line 332) | def write_long(self, value: int):
    method write_ulong (line 335) | def write_ulong(self, value: int):
    method write_list (line 338) | def write_list(
    method write_int_list (line 357) | def write_int_list(
    method write_bool_list (line 365) | def write_bool_list(
    method write_string_list (line 373) | def write_string_list(
    method write_byte_list (line 381) | def write_byte_list(
    method write_short_list (line 389) | def write_short_list(
    method read_bool (line 397) | def read_bool(self) -> bool:
    method write_bool (line 400) | def write_bool(self, value: bool):
    method read_int_tuple (line 403) | def read_int_tuple(self) -> tuple[int, int]:
    method read_int_tuple_list (line 406) | def read_int_tuple_list(
    method write_int_tuple (line 416) | def write_int_tuple(self, value: tuple[int, int]):
    method write_int_tuple_list (line 420) | def write_int_tuple_list(
    method read_int_bool_dict (line 428) | def read_int_bool_dict(self, length: int | None = None) -> dict[int, b...
    method write_int_bool_dict (line 438) | def write_int_bool_dict(
    method read_int_int_dict (line 447) | def read_int_int_dict(self, length: int | None = None) -> dict[int, int]:
    method write_int_int_dict (line 457) | def write_int_int_dict(
    method read_int_double_dict (line 466) | def read_int_double_dict(
    method write_int_double_dict (line 478) | def write_int_double_dict(
    method read_short_bool_dict (line 487) | def read_short_bool_dict(
    method write_short_bool_dict (line 499) | def write_short_bool_dict(
    method unpad_pkcs7 (line 508) | def unpad_pkcs7(self) -> Data | None:
    method split (line 519) | def split(self, separator: bytes, maxsplit: int = -1) -> list[Data]:
    method to_int (line 525) | def to_int(self) -> int:
    method to_int_little (line 531) | def to_int_little(self) -> int:
    method to_str (line 534) | def to_str(self) -> str:
    method to_bool (line 540) | def to_bool(self) -> bool:
    method int_list_data_list (line 544) | def int_list_data_list(int_list: list[int]) -> list[Data]:
    method string_list_data_list (line 551) | def string_list_data_list(string_list: list[Any]) -> list[Data]:
    method data_list_int_list (line 558) | def data_list_int_list(data_list: list[Data]) -> list[int]:
    method data_list_string_list (line 565) | def data_list_string_list(data_list: list[Data]) -> list[str]:
    method to_bytes (line 571) | def to_bytes(self) -> bytes:
    method from_many (line 575) | def from_many(others: list[Data], joiner: Data | None = None) -> Data:
    method from_int_list (line 585) | def from_int_list(
    method strip (line 593) | def strip(self) -> Data:
    method replace (line 596) | def replace(self, old_data: Data, new_data: Data) -> Data:
    method set (line 599) | def set(self, value: bytes | str | None | int | bool) -> None:
    method to_bytes_io (line 602) | def to_bytes_io(self) -> BytesIO:
    method __repr__ (line 605) | def __repr__(self) -> str:
    method __str__ (line 608) | def __str__(self) -> str:
    method to_base_64 (line 611) | def to_base_64(self) -> str:
    method from_base_64 (line 615) | def from_base_64(string: str) -> Data:
    method to_csv (line 618) | def to_csv(self, *args: Any, **kwargs: Any) -> core.CSV:
    method search (line 621) | def search(self, search_data: Data, start: int = 0) -> int:
    method add_line (line 624) | def add_line(
  class PaddedInt (line 632) | class PaddedInt:
    method __init__ (line 633) | def __init__(self, value: int, size: int):
    method __int__ (line 637) | def __int__(self):
    method __str__ (line 640) | def __str__(self):
    method __repr__ (line 643) | def __repr__(self):
    method to_str (line 646) | def to_str(self):

FILE: src/bcsfe/core/io/git_handler.py
  class Repo (line 6) | class Repo:
    method __init__ (line 7) | def __init__(self, url: str, output_error: bool = True):
    method get_repo_name (line 12) | def get_repo_name(self) -> str:
    method get_path (line 15) | def get_path(self) -> core.Path:
    method run_cmd (line 20) | def run_cmd(self, cmd: str) -> bool:
    method clone_to_temp (line 27) | def clone_to_temp(self, path: core.Path) -> bool:
    method clone (line 31) | def clone(self) -> bool:
    method pull (line 40) | def pull(self) -> bool:
    method fetch (line 44) | def fetch(self) -> bool:
    method get_file (line 48) | def get_file(self, file_path: core.Path) -> core.Data | None:
    method get_temp_file (line 55) | def get_temp_file(self, temp_folder: core.Path, file_path: core.Path) ...
    method get_folder (line 59) | def get_folder(self, folder_path: core.Path) -> core.Path | None:
    method is_cloned (line 65) | def is_cloned(self) -> bool:
  class GitHandler (line 72) | class GitHandler:
    method get_repo_folder (line 74) | def get_repo_folder() -> core.Path:
    method get_repo (line 79) | def get_repo(self, repo_url: str, output_error: bool = True) -> Repo |...
    method is_git_installed (line 88) | def is_git_installed() -> bool:

FILE: src/bcsfe/core/io/json_file.py
  class JsonFile (line 7) | class JsonFile:
    method __init__ (line 8) | def __init__(self, data: core.Data):
    method from_path (line 12) | def from_path(path: core.Path) -> JsonFile:
    method from_object (line 16) | def from_object(js: Any) -> JsonFile:
    method from_data (line 20) | def from_data(data: core.Data) -> JsonFile:
    method to_data (line 23) | def to_data(self, indent: int | None = 4) -> core.Data:
    method to_file (line 26) | def to_file(self, path: core.Path) -> None:
    method to_object (line 29) | def to_object(self) -> Any:
    method get (line 32) | def get(self, key: str) -> Any:
    method set (line 35) | def set(self, key: str, value: Any) -> None:
    method __str__ (line 38) | def __str__(self) -> str:
    method __getitem__ (line 41) | def __getitem__(self, key: str) -> Any:
    method __setitem__ (line 44) | def __setitem__(self, key: str, value: Any) -> None:

FILE: src/bcsfe/core/io/path.py
  class Path (line 10) | class Path:
    method __init__ (line 11) | def __init__(self, path: str = "", is_relative: bool = False):
    method is_relative (line 19) | def is_relative(self) -> bool:
    method get_root (line 23) | def get_root() -> Path:
    method get_relative_path (line 26) | def get_relative_path(self, path: str) -> str:
    method get_files_folder (line 30) | def get_files_folder() -> Path:
    method strip_trailing_slash (line 38) | def strip_trailing_slash(self) -> Path:
    method open (line 41) | def open(self):
    method open_file (line 53) | def open_file(self):
    method run (line 65) | def run(self, arg: str = "", display_output: bool = False) -> core.Com...
    method to_str (line 70) | def to_str(self) -> str:
    method to_str_forwards (line 73) | def to_str_forwards(self) -> str:
    method get_data_folder (line 77) | def get_data_folder(app_name: str = "bcsfe") -> Path:
    method get_config_folder (line 95) | def get_config_folder(app_name: str = "bcsfe") -> Path:
    method get_state_folder (line 108) | def get_state_folder(app_name: str = "bcsfe") -> Path:
    method is_empty (line 120) | def is_empty(self) -> bool:
    method generate_dirs (line 123) | def generate_dirs(self: Path) -> Path:
    method create (line 133) | def create(self) -> Path:
    method exists (line 138) | def exists(self) -> bool:
    method __make_dirs (line 141) | def __make_dirs(self) -> Path:
    method basename (line 145) | def basename(self) -> str:
    method join (line 149) | def join(*paths: str | Path) -> Path:
    method add (line 153) | def add(self, *paths: str | Path) -> Path:
    method strip_leading_slash (line 157) | def strip_leading_slash(self) -> Path:
    method __str__ (line 160) | def __str__(self) -> str:
    method __repr__ (line 163) | def __repr__(self) -> str:
    method remove_tree (line 166) | def remove_tree(self, ignoreErrors: bool = False) -> Path:
    method remove (line 171) | def remove(self):
    method has_files (line 178) | def has_files(self) -> bool:
    method copy (line 181) | def copy(self, target: Path):
    method copy_thread (line 194) | def copy_thread(self, target: Path):
    method copy_tree (line 197) | def copy_tree(self, target: Path):
    method read (line 204) | def read(self, create: bool = False) -> core.Data:
    method write (line 213) | def write(self, data: core.Data):
    method get_paths_dir (line 216) | def get_paths_dir(self, regex: str | None = None) -> list[Path]:
    method get_files (line 228) | def get_files(self, regex: str | None = None) -> list[Path]:
    method is_file (line 231) | def is_file(self) -> bool:
    method get_dirs (line 234) | def get_dirs(self) -> list["Path"]:
    method glob (line 237) | def glob(self, pattern: str, recursive: bool = False) -> list[Path]:
    method strip_path_from (line 243) | def strip_path_from(self, path: Path) -> Path:
    method is_directory (line 246) | def is_directory(self) -> bool:
    method change_name (line 249) | def change_name(self, name: str) -> Path:
    method rename (line 252) | def rename(self, name: str, overwrite: bool = False):
    method parent (line 266) | def parent(self) -> Path:
    method change_extension (line 269) | def change_extension(self, extension: str) -> Path:
    method remove_extension (line 274) | def remove_extension(self) -> Path:
    method get_extension (line 277) | def get_extension(self) -> str:
    method get_file_name (line 283) | def get_file_name(self) -> str:
    method get_file_name_path (line 286) | def get_file_name_path(self) -> Path:
    method get_file_name_without_extension (line 289) | def get_file_name_without_extension(self) -> str:
    method get_file_size (line 292) | def get_file_size(self) -> int:
    method get_absolute_path (line 295) | def get_absolute_path(self) -> Path:
    method copy_object (line 298) | def copy_object(self) -> Path:

FILE: src/bcsfe/core/io/root_handler.py
  class PackageNameNotSet (line 6) | class PackageNameNotSet(Exception):
  class RootHandler (line 10) | class RootHandler:
    method __init__ (line 11) | def __init__(self):
    method is_android (line 14) | def is_android(self) -> bool:
    method set_package_name (line 17) | def set_package_name(self, package_name: str):
    method is_rooted (line 20) | def is_rooted(self) -> bool:
    method get_battlecats_packages (line 27) | def get_battlecats_packages(self) -> list[str]:
    method get_package_name (line 36) | def get_package_name(self) -> str:
    method get_battlecats_path (line 41) | def get_battlecats_path(self) -> core.Path:
    method get_battlecats_save_path (line 44) | def get_battlecats_save_path(self) -> core.Path:
    method save_battlecats_save (line 47) | def save_battlecats_save(self, local_path: core.Path) -> core.CommandR...
    method load_battlecats_save (line 51) | def load_battlecats_save(self, local_path: core.Path) -> core.CommandR...
    method close_game (line 55) | def close_game(self) -> core.CommandResult:
    method run_game (line 61) | def run_game(self) -> core.CommandResult:
    method rerun_game (line 67) | def rerun_game(self) -> core.CommandResult:
    method save_locally (line 77) | def save_locally(
    method load_locally (line 89) | def load_locally(self, local_path: core.Path) -> core.CommandResult:
    method load_save (line 100) | def load_save(

FILE: src/bcsfe/core/io/save.py
  class SaveError (line 11) | class SaveError(Exception):
  class CantDetectSaveCCError (line 15) | class CantDetectSaveCCError(SaveError):
  class SaveFileInvalid (line 19) | class SaveFileInvalid(SaveError):
  class FailedToLoadError (line 23) | class FailedToLoadError(SaveError):
  class FailedToSaveError (line 27) | class FailedToSaveError(SaveError):
  class SaveFile (line 31) | class SaveFile:
    method __init__ (line 32) | def __init__(
    method get_localizable (line 65) | def get_localizable(self) -> core.Localizable:
    method load_save_file (line 70) | def load_save_file(self, other: SaveFile):
    method detect_cc (line 77) | def detect_cc(self) -> core.CountryCode | None:
    method get_salt (line 84) | def get_salt(self) -> str:
    method get_current_hash (line 93) | def get_current_hash(self) -> str:
    method get_new_hash (line 104) | def get_new_hash(self, existing_hash: bool = True) -> str:
    method set_hash (line 120) | def set_hash(self, add: bool = False):
    method verify_hash (line 129) | def verify_hash(self) -> bool:
    method load_wrapper (line 137) | def load_wrapper(self):
    method set_gv (line 151) | def set_gv(self, gv: core.GameVersion):
    method set_cc (line 154) | def set_cc(self, cc: core.CountryCode):
    method set_package_name (line 158) | def set_package_name(self, package_name: str | None):
    method load (line 161) | def load(self):
    method save (line 1265) | def save(self, data: core.Data):
    method to_data (line 2237) | def to_data(self) -> core.Data:
    method save_wrapper (line 2243) | def save_wrapper(self, data: core.Data) -> None:
    method to_file_thread (line 2251) | def to_file_thread(self, path: core.Path):
    method to_file (line 2254) | def to_file(self, path: core.Path) -> None:
    method get_temp_path (line 2263) | def get_temp_path() -> core.Path:
    method to_dict (line 2268) | def to_dict(self) -> dict[str, Any]:
    method from_dict (line 2595) | def from_dict(data: dict[str, Any], warn: bool = True) -> SaveFile:
    method init_save (line 2983) | def init_save(self, gv: core.GameVersion | None = None):
    method is_jp (line 3368) | def is_jp(self) -> bool:
    method not_jp (line 3371) | def not_jp(self) -> bool:
    method is_en (line 3374) | def is_en(self) -> bool:
    method should_read_dst (line 3377) | def should_read_dst(self) -> bool:
    method read_dst (line 3382) | def read_dst(self):
    method write_dst (line 3386) | def write_dst(self):
    method calculate_user_rank (line 3394) | def calculate_user_rank(self):
    method get_string_identifier (line 3411) | def get_string_identifier(identifier: str) -> str:
    method store_string (line 3414) | def store_string(self, identifier: str, string: str, overwrite: bool =...
    method get_string (line 3424) | def get_string(self, identifier: str) -> str | None:
    method get_strings (line 3430) | def get_strings(self, identifier: str) -> list[str]:
    method remove_string (line 3438) | def remove_string(self, identifier: str):
    method remove_strings (line 3444) | def remove_strings(self, identifier: str):
    method store_dict (line 3451) | def store_dict(
    method get_dict (line 3467) | def get_dict(self, identifier: str) -> dict[str, str] | None:
    method remove_dict (line 3475) | def remove_dict(self, identifier: str):
    method get_saves_path (line 3483) | def get_saves_path() -> core.Path:
    method get_save_path (line 3487) | def get_save_path() -> core.Path:
    method get_default_path (line 3490) | def get_default_path(self) -> core.Path:
    method check_backups (line 3505) | def check_backups():
    method unlock_equip_menu (line 3536) | def unlock_equip_menu(self):
    method get_xp (line 3539) | def get_xp(self) -> int:
    method set_xp (line 3542) | def set_xp(self, xp: int):
    method get_catfood (line 3545) | def get_catfood(self) -> int:
    method set_catfood (line 3548) | def set_catfood(self, catfood: int):
    method get_normal_tickets (line 3551) | def get_normal_tickets(self) -> int:
    method set_normal_tickets (line 3554) | def set_normal_tickets(self, normal_tickets: int):
    method get_rare_tickets (line 3557) | def get_rare_tickets(self) -> int:
    method set_rare_tickets (line 3560) | def set_rare_tickets(self, rare_tickets: int):
    method get_platinum_tickets (line 3563) | def get_platinum_tickets(self) -> int:
    method set_platinum_tickets (line 3566) | def set_platinum_tickets(self, platinum_tickets: int):
    method get_legend_tickets (line 3569) | def get_legend_tickets(self) -> int:
    method set_legend_tickets (line 3572) | def set_legend_tickets(self, legend_tickets: int):
    method get_platinum_shards (line 3575) | def get_platinum_shards(self) -> int:
    method set_platinum_shards (line 3578) | def set_platinum_shards(self, platinum_shards: int):
    method get_np (line 3581) | def get_np(self) -> int:
    method set_np (line 3584) | def set_np(self, np: int):
    method get_leadership (line 3587) | def get_leadership(self) -> int:
    method set_leadership (line 3590) | def set_leadership(self, leadership: int):
    method max_rank_up_sale (line 3593) | def max_rank_up_sale(self):

FILE: src/bcsfe/core/io/thread_helper.py
  class Thread (line 6) | class Thread:
    method __init__ (line 7) | def __init__(
    method start (line 18) | def start(self):
    method join (line 24) | def join(self):
    method is_alive (line 28) | def is_alive(self) -> bool:
    method run (line 34) | def run(name: str, target: Callable[..., None], args: Any):
  function thread_run_many_helper (line 40) | def thread_run_many_helper(funcs: list[Callable[..., Any]], *args: list[...
  function thread_run_many (line 47) | def thread_run_many(

FILE: src/bcsfe/core/io/waydroid.py
  class WayDroidNotInstalledError (line 9) | class WayDroidNotInstalledError(Exception):
    method __init__ (line 10) | def __init__(self, result: CommandResult):
  class WayDroidHandler (line 14) | class WayDroidHandler(io.root_handler.RootHandler):
    method __init__ (line 15) | def __init__(self):
    method set_package_name (line 22) | def set_package_name(self, package_name: str):
    method display_waydroid_not_installed (line 27) | def display_waydroid_not_installed(e: WayDroidNotInstalledError):
    method check_waydroid_installed (line 32) | def check_waydroid_installed():
    method run_shell_cmd (line 37) | def run_shell_cmd(self, command: str) -> core.CommandResult:
    method pull_file (line 44) | def pull_file(
    method push_file (line 68) | def push_file(
    method get_battlecats_packages (line 96) | def get_battlecats_packages(self) -> list[str]:
    method save_battlecats_save (line 114) | def save_batt
Condensed preview — 208 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,285K chars).
[
  {
    "path": "CHANGELOG.md",
    "chars": 19911,
    "preview": "# Changelog\n\n## [3.3.0] - 2026-04-01\n\n### Added\n\n- Display basic save info when a save is loaded (region, version, parti"
  },
  {
    "path": "LICENSE",
    "chars": 35150,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "LOCALIZATION.md",
    "chars": 2485,
    "preview": "# Localization\n\nSmall tutorial on how to localize the editor into a different language.\n\n## Disclaimer\n\nPlease do not us"
  },
  {
    "path": "MANIFEST.in",
    "chars": 127,
    "preview": "recursive-include src/bcsfe/files *\nrecursive-exclude src/bcsfe/files/game_data *\nrecursive-exclude src/bcsfe/files/map_"
  },
  {
    "path": "README.md",
    "chars": 13823,
    "preview": "# Battle Cats Save File Editor\n\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/fieryhenry)\n\n[!["
  },
  {
    "path": "pyproject.toml",
    "chars": 1165,
    "preview": "[build-system]\nrequires = [\"setuptools\", \"wheel\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"bcsfe\"\nauth"
  },
  {
    "path": "requirements.txt",
    "chars": 113,
    "preview": "aenum==3.1.16\ncolored==1.4.4\nPyJWT==2.12.1\nPyYAML==6.0.2\nRequests==2.33.1\nbeautifulsoup4==4.13.4\nargparse==1.4.0\n"
  },
  {
    "path": "setup.py",
    "chars": 38,
    "preview": "from setuptools import setup\n\nsetup()\n"
  },
  {
    "path": "src/bcsfe/__init__.py",
    "chars": 144,
    "preview": "__version__ = \"3.3.0\"\n\nfrom bcsfe import core, cli\n\n\n__all__ = [\"core\", \"cli\"]\n\n\ndef run():\n    from bcsfe import __main"
  },
  {
    "path": "src/bcsfe/__main__.py",
    "chars": 2091,
    "preview": "from __future__ import annotations\nimport traceback\n\nfrom bcsfe import cli\n\nfrom bcsfe import core\n\nimport bcsfe\nimport "
  },
  {
    "path": "src/bcsfe/cli/__init__.py",
    "chars": 349,
    "preview": "from bcsfe.cli import (\n    color,\n    dialog_creator,\n    main,\n    file_dialog,\n    feature_handler,\n    save_manageme"
  },
  {
    "path": "src/bcsfe/cli/color.py",
    "chars": 5575,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom aenum import NamedConstant  # type: ignore\nimport colored"
  },
  {
    "path": "src/bcsfe/cli/dialog_creator.py",
    "chars": 23917,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color\n\n\nclass Ran"
  },
  {
    "path": "src/bcsfe/cli/edits/__init__.py",
    "chars": 417,
    "preview": "from bcsfe.cli.edits import (\n    basic_items,\n    cat_editor,\n    clear_tutorial,\n    rare_ticket_trade,\n    fixes,\n   "
  },
  {
    "path": "src/bcsfe/cli/edits/aku_realm.py",
    "chars": 337,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import color\n\n\ndef unlock_aku_realm(save_file: "
  },
  {
    "path": "src/bcsfe/cli/edits/basic_items.py",
    "chars": 15417,
    "preview": "from __future__ import annotations\nimport random\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator, color, edi"
  },
  {
    "path": "src/bcsfe/cli/edits/cat_editor.py",
    "chars": 28604,
    "preview": "from __future__ import annotations\nimport enum\nfrom typing import Any, Callable\n\nfrom bcsfe import core\nfrom bcsfe.cli i"
  },
  {
    "path": "src/bcsfe/cli/edits/clear_tutorial.py",
    "chars": 314,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import color\n\n\ndef clear_tutorial(\n    save_fil"
  },
  {
    "path": "src/bcsfe/cli/edits/enemy_editor.py",
    "chars": 6963,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cre"
  },
  {
    "path": "src/bcsfe/cli/edits/event_tickets.py",
    "chars": 6164,
    "preview": "from __future__ import annotations\nfrom bcsfe import cli, core\nfrom bcsfe.core.game.catbase.gatya import GatyaEventType\n"
  },
  {
    "path": "src/bcsfe/cli/edits/fixes.py",
    "chars": 1509,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import color\nimport datetime\n\n\nclass Fixes:\n   "
  },
  {
    "path": "src/bcsfe/cli/edits/map.py",
    "chars": 16385,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_creator\nfrom typing import"
  },
  {
    "path": "src/bcsfe/cli/edits/max_all.py",
    "chars": 4638,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable\nfrom bcsfe import core\n\n\ndef max_catfood(save_f"
  },
  {
    "path": "src/bcsfe/cli/edits/rare_ticket_trade.py",
    "chars": 1405,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\nfrom bcsfe.cli import color, dialog_creator\n\n\nclass RareTicke"
  },
  {
    "path": "src/bcsfe/cli/edits/storage.py",
    "chars": 6080,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_creator\nfrom bcsfe.cli.edi"
  },
  {
    "path": "src/bcsfe/cli/feature_handler.py",
    "chars": 15162,
    "preview": "from __future__ import annotations\nfrom typing import Any, Callable\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_"
  },
  {
    "path": "src/bcsfe/cli/file_dialog.py",
    "chars": 5070,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_creator\n\n\nclass FileDialog"
  },
  {
    "path": "src/bcsfe/cli/main.py",
    "chars": 11730,
    "preview": "from __future__ import annotations\n\n\"\"\"Main class for the CLI.\"\"\"\n\nimport sys\nimport traceback\nfrom typing import Any, N"
  },
  {
    "path": "src/bcsfe/cli/recent_saves.py",
    "chars": 4053,
    "preview": "from __future__ import annotations\nfrom typing import Any\n\nfrom bcsfe import core\nimport datetime\nimport json\nfrom bcsfe"
  },
  {
    "path": "src/bcsfe/cli/save_management.py",
    "chars": 23067,
    "preview": "from __future__ import annotations\nimport datetime\nfrom bcsfe import core\nimport bcsfe\nfrom bcsfe.core import io\nfrom bc"
  },
  {
    "path": "src/bcsfe/cli/server_cli.py",
    "chars": 2571,
    "preview": "from __future__ import annotations\nfrom bcsfe.cli import dialog_creator, main, color, file_dialog\nfrom bcsfe import core"
  },
  {
    "path": "src/bcsfe/core/__init__.py",
    "chars": 13372,
    "preview": "from __future__ import annotations\nfrom typing import Any\n\nfrom requests.exceptions import ConnectionError\nfrom requests"
  },
  {
    "path": "src/bcsfe/core/country_code.py",
    "chars": 2946,
    "preview": "from __future__ import annotations\nimport enum\nfrom bcsfe.cli import dialog_creator\nfrom bcsfe import core\n\n\nclass Count"
  },
  {
    "path": "src/bcsfe/core/crypto.py",
    "chars": 4646,
    "preview": "from __future__ import annotations\nimport enum\nimport hashlib\nimport hmac\nimport random\nfrom bcsfe import core\n\n\nclass H"
  },
  {
    "path": "src/bcsfe/core/game/__init__.py",
    "chars": 135,
    "preview": "from bcsfe.core.game import catbase, battle, map, gamoto, localizable\n\n__all__ = [\"catbase\", \"battle\", \"map\", \"gamoto\", "
  },
  {
    "path": "src/bcsfe/core/game/battle/__init__.py",
    "chars": 124,
    "preview": "from bcsfe.core.game.battle import slots, battle_items, cleared_slots\n\n__all__ = [\"slots\", \"battle_items\", \"cleared_slot"
  },
  {
    "path": "src/bcsfe/core/game/battle/battle_items.py",
    "chars": 9378,
    "preview": "from __future__ import annotations\n\nimport datetime\nfrom math import inf, isnan\nimport math\nfrom typing import Any\nfrom "
  },
  {
    "path": "src/bcsfe/core/game/battle/cleared_slots.py",
    "chars": 8464,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom typing import Any\n\n\nclass CatSlot:\n    def __init__(self,"
  },
  {
    "path": "src/bcsfe/core/game/battle/enemy.py",
    "chars": 4079,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass Enemy:\n    def __init__(self, id: int):\n        self.i"
  },
  {
    "path": "src/bcsfe/core/game/battle/slots.py",
    "chars": 6205,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator\n\n\n"
  },
  {
    "path": "src/bcsfe/core/game/catbase/__init__.py",
    "chars": 803,
    "preview": "from bcsfe.core.game.catbase import (\n    gatya_item,\n    stamp,\n    cat,\n    upgrade,\n    special_skill,\n    my_sale,\n "
  },
  {
    "path": "src/bcsfe/core/game/catbase/beacon_base.py",
    "chars": 2234,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass BeaconEventListScene:\n    def _"
  },
  {
    "path": "src/bcsfe/core/game/catbase/cat.py",
    "chars": 41613,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass SkillLevel:\n    def __init__(\n "
  },
  {
    "path": "src/bcsfe/core/game/catbase/drop_chara.py",
    "chars": 2029,
    "preview": "from __future__ import annotations\nfrom dataclasses import dataclass\n\nfrom bcsfe import core\n\n\n@dataclass\nclass Drop:\n  "
  },
  {
    "path": "src/bcsfe/core/game/catbase/gambling.py",
    "chars": 3265,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom typing import Any\n\nfrom bcsfe.cli import color\n\n\nclass Ga"
  },
  {
    "path": "src/bcsfe/core/game/catbase/gatya.py",
    "chars": 14293,
    "preview": "from __future__ import annotations\nimport enum\nfrom typing import Any, Callable\nfrom bcsfe import core\nfrom bcsfe.cli im"
  },
  {
    "path": "src/bcsfe/core/game/catbase/gatya_item.py",
    "chars": 4657,
    "preview": "from __future__ import annotations\nimport enum\nfrom bcsfe import core\n\n\nclass GatyaItemNames:\n    def __init__(self, sav"
  },
  {
    "path": "src/bcsfe/core/game/catbase/item_pack.py",
    "chars": 5856,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass PurchasedPack:\n    def __init__"
  },
  {
    "path": "src/bcsfe/core/game/catbase/login_bonuses.py",
    "chars": 5057,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass Login:\n    def __init__(self, c"
  },
  {
    "path": "src/bcsfe/core/game/catbase/matatabi.py",
    "chars": 1982,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass Fruit:\n    def __init__(\n        self,\n        id: int"
  },
  {
    "path": "src/bcsfe/core/game/catbase/medals.py",
    "chars": 5537,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cre"
  },
  {
    "path": "src/bcsfe/core/game/catbase/mission.py",
    "chars": 11231,
    "preview": "from __future__ import annotations\nfrom typing import Any\n\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cr"
  },
  {
    "path": "src/bcsfe/core/game/catbase/my_sale.py",
    "chars": 1847,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass MySale:\n    def __init__(self, "
  },
  {
    "path": "src/bcsfe/core/game/catbase/nyanko_club.py",
    "chars": 8442,
    "preview": "from __future__ import annotations\nimport datetime\nimport random\nimport time\nfrom typing import Any\n\nfrom bcsfe import c"
  },
  {
    "path": "src/bcsfe/core/game/catbase/officer_pass.py",
    "chars": 2375,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color\n\n\nclass Off"
  },
  {
    "path": "src/bcsfe/core/game/catbase/playtime.py",
    "chars": 2519,
    "preview": "from __future__ import annotations\nfrom dataclasses import dataclass\n\nfrom bcsfe import core\nfrom bcsfe.cli import color"
  },
  {
    "path": "src/bcsfe/core/game/catbase/powerup.py",
    "chars": 6238,
    "preview": "from __future__ import annotations\n\nfrom bcsfe import core\n\n\nclass PowerUpHelper:\n    def __init__(self, cat: core.Cat, "
  },
  {
    "path": "src/bcsfe/core/game/catbase/scheme_items.py",
    "chars": 6344,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator, color\n\n\nclass SchemeData"
  },
  {
    "path": "src/bcsfe/core/game/catbase/special_skill.py",
    "chars": 10736,
    "preview": "from __future__ import annotations, division\nfrom bcsfe import core\n\nfrom typing import Any\n\nfrom bcsfe.cli import dialo"
  },
  {
    "path": "src/bcsfe/core/game/catbase/stamp.py",
    "chars": 1865,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass StampData:\n    def __init__(\n  "
  },
  {
    "path": "src/bcsfe/core/game/catbase/talent_orbs.py",
    "chars": 22276,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cre"
  },
  {
    "path": "src/bcsfe/core/game/catbase/unlock_popups.py",
    "chars": 4406,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass Popup:\n    def __init__(self, seen: bool):\n        sel"
  },
  {
    "path": "src/bcsfe/core/game/catbase/upgrade.py",
    "chars": 6366,
    "preview": "from __future__ import annotations\nimport random\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import col"
  },
  {
    "path": "src/bcsfe/core/game/catbase/user_rank_rewards.py",
    "chars": 8894,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator, color\n\n\nclass RankGift:\n"
  },
  {
    "path": "src/bcsfe/core/game/gamoto/__init__.py",
    "chars": 191,
    "preview": "from bcsfe.core.game.gamoto import (\n    catamins,\n    gamatoto,\n    base_materials,\n    ototo,\n    cat_shrine,\n)\n\n__all"
  },
  {
    "path": "src/bcsfe/core/game/gamoto/base_materials.py",
    "chars": 2602,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator\n\n\nclass Material:\n    def"
  },
  {
    "path": "src/bcsfe/core/game/gamoto/cat_shrine.py",
    "chars": 6652,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cre"
  },
  {
    "path": "src/bcsfe/core/game/gamoto/catamins.py",
    "chars": 1526,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass Catamin:\n    def __init__(self, amount: int):\n        "
  },
  {
    "path": "src/bcsfe/core/game/gamoto/gamatoto.py",
    "chars": 15398,
    "preview": "from __future__ import annotations\nfrom dataclasses import dataclass\nfrom typing import Any\nfrom bcsfe import core\nfrom "
  },
  {
    "path": "src/bcsfe/core/game/gamoto/ototo.py",
    "chars": 23287,
    "preview": "from __future__ import annotations\nfrom dataclasses import dataclass\nfrom typing import Any\nfrom bcsfe import core\nfrom "
  },
  {
    "path": "src/bcsfe/core/game/localizable.py",
    "chars": 904,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass Localizable:\n    def __init__(self, save_file: core.Sa"
  },
  {
    "path": "src/bcsfe/core/game/map/__init__.py",
    "chars": 633,
    "preview": "from bcsfe.core.game.map import (\n    story,\n    event,\n    item_reward_stage,\n    timed_score,\n    ex_stage,\n    dojo,\n"
  },
  {
    "path": "src/bcsfe/core/game/map/aku.py",
    "chars": 6865,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color\n\n\nclass Sta"
  },
  {
    "path": "src/bcsfe/core/game/map/challenge.py",
    "chars": 2179,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator\n\n\n"
  },
  {
    "path": "src/bcsfe/core/game/map/chapters.py",
    "chars": 12212,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import edits\n\n\nclass Sta"
  },
  {
    "path": "src/bcsfe/core/game/map/dojo.py",
    "chars": 11638,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator\n\n\n"
  },
  {
    "path": "src/bcsfe/core/game/map/enigma.py",
    "chars": 6989,
    "preview": "from __future__ import annotations\nimport time\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import dialo"
  },
  {
    "path": "src/bcsfe/core/game/map/event.py",
    "chars": 33684,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cre"
  },
  {
    "path": "src/bcsfe/core/game/map/ex_stage.py",
    "chars": 2740,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass Stage:\n    def __init__(self, clear_amount: int):\n    "
  },
  {
    "path": "src/bcsfe/core/game/map/gauntlets.py",
    "chars": 11852,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import edits\n\n\nclass Sta"
  },
  {
    "path": "src/bcsfe/core/game/map/item_reward_stage.py",
    "chars": 12627,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass Stage:\n    def __init__(self, c"
  },
  {
    "path": "src/bcsfe/core/game/map/legend_quest.py",
    "chars": 12620,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import edits\n\n\nclass Sta"
  },
  {
    "path": "src/bcsfe/core/game/map/map_names.py",
    "chars": 3267,
    "preview": "from __future__ import annotations\n\nfrom bcsfe import core\n\n\nclass MapNames:\n    def __init__(\n        self,\n        sav"
  },
  {
    "path": "src/bcsfe/core/game/map/map_option.py",
    "chars": 2207,
    "preview": "from __future__ import annotations\n\nfrom bcsfe import core\n\n\nclass MapOptionLine:\n    def __init__(\n        self,\n      "
  },
  {
    "path": "src/bcsfe/core/game/map/map_reset.py",
    "chars": 3795,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass MapResetData:\n    def __init__(\n        self,\n        "
  },
  {
    "path": "src/bcsfe/core/game/map/outbreaks.py",
    "chars": 8480,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import dialog_creator, c"
  },
  {
    "path": "src/bcsfe/core/game/map/story.py",
    "chars": 43931,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cre"
  },
  {
    "path": "src/bcsfe/core/game/map/timed_score.py",
    "chars": 5730,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\n\n\nclass Stage:\n    def __init__(self, score: int):\n        sel"
  },
  {
    "path": "src/bcsfe/core/game/map/tower.py",
    "chars": 2259,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass TowerChapters:\n    def __init__"
  },
  {
    "path": "src/bcsfe/core/game/map/uncanny.py",
    "chars": 3765,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color, dialog_cre"
  },
  {
    "path": "src/bcsfe/core/game/map/zero_legends.py",
    "chars": 9862,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import edits, color\n\n\ncl"
  },
  {
    "path": "src/bcsfe/core/game_version.py",
    "chars": 7606,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass GameVersion:\n    \"\"\"A class to "
  },
  {
    "path": "src/bcsfe/core/io/__init__.py",
    "chars": 425,
    "preview": "from bcsfe.core.io import (\n    bc_csv,\n    path,\n    data,\n    command,\n    yaml,\n    config,\n    json_file,\n    save,\n"
  },
  {
    "path": "src/bcsfe/core/io/adb_handler.py",
    "chars": 6533,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.core import io\nfrom bcsfe.cli import dialog_creator"
  },
  {
    "path": "src/bcsfe/core/io/bc_csv.py",
    "chars": 7085,
    "preview": "from __future__ import annotations\nimport csv as csv_module\nimport enum\nfrom typing import Any\nimport typing\nfrom bcsfe "
  },
  {
    "path": "src/bcsfe/core/io/command.py",
    "chars": 1458,
    "preview": "from __future__ import annotations\nimport subprocess\nimport threading\n\n\nclass CommandResult:\n    def __init__(self, resu"
  },
  {
    "path": "src/bcsfe/core/io/config.py",
    "chars": 16184,
    "preview": "from __future__ import annotations\nimport enum\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe.cli import color"
  },
  {
    "path": "src/bcsfe/core/io/data.py",
    "chars": 19024,
    "preview": "from __future__ import annotations\nimport base64\nimport enum\nfrom io import BytesIO\nimport struct\nimport typing\nfrom typ"
  },
  {
    "path": "src/bcsfe/core/io/git_handler.py",
    "chars": 2722,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nfrom bcsfe.cli import color\n\n\nclass Repo:\n    def __init__(sel"
  },
  {
    "path": "src/bcsfe/core/io/json_file.py",
    "chars": 1146,
    "preview": "from __future__ import annotations\nimport json\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass JsonFile:\n    def _"
  },
  {
    "path": "src/bcsfe/core/io/path.py",
    "chars": 9636,
    "preview": "from __future__ import annotations\nimport glob\nimport os\nimport shutil\n\nfrom bcsfe import core\nimport re\n\n\nclass Path:\n "
  },
  {
    "path": "src/bcsfe/core/io/root_handler.py",
    "chars": 3661,
    "preview": "from __future__ import annotations\nfrom bcsfe import core\nimport tempfile\n\n\nclass PackageNameNotSet(Exception):\n    pass"
  },
  {
    "path": "src/bcsfe/core/io/save.py",
    "chars": 133363,
    "preview": "from __future__ import annotations\nimport base64\nfrom typing import Any\nfrom bcsfe import core, __version__, cli\nimport "
  },
  {
    "path": "src/bcsfe/core/io/thread_helper.py",
    "chars": 2036,
    "preview": "from __future__ import annotations\nfrom typing import Callable, Any, Iterable\nimport threading\n\n\nclass Thread:\n    def _"
  },
  {
    "path": "src/bcsfe/core/io/waydroid.py",
    "chars": 3861,
    "preview": "from __future__ import annotations\n\nfrom bcsfe import core\nfrom bcsfe.cli import color\nfrom bcsfe.core import io\nfrom bc"
  },
  {
    "path": "src/bcsfe/core/io/yaml.py",
    "chars": 1841,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\nfrom bcsfe import cli\nimport yaml\n\n\ncla"
  },
  {
    "path": "src/bcsfe/core/locale_handler.py",
    "chars": 23586,
    "preview": "from __future__ import annotations\nimport dataclasses\nimport tempfile\nfrom typing import Any\nfrom bcsfe import core\nfrom"
  },
  {
    "path": "src/bcsfe/core/log.py",
    "chars": 3298,
    "preview": "from __future__ import annotations\n\n\"\"\"Module for handling logging\"\"\"\nimport traceback\nfrom bcsfe import core\nimport tim"
  },
  {
    "path": "src/bcsfe/core/max_value_helper.py",
    "chars": 2201,
    "preview": "from __future__ import annotations\nimport enum\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass MaxValueType(enum.E"
  },
  {
    "path": "src/bcsfe/core/server/__init__.py",
    "chars": 328,
    "preview": "from bcsfe.core.server import (\n    managed_item,\n    headers,\n    client_info,\n    server_handler,\n    game_data_getter"
  },
  {
    "path": "src/bcsfe/core/server/client_info.py",
    "chars": 927,
    "preview": "from __future__ import annotations\nfrom typing import Any\nfrom bcsfe import core\n\n\nclass ClientInfo:\n    def __init__(se"
  },
  {
    "path": "src/bcsfe/core/server/event_data.py",
    "chars": 10243,
    "preview": "from __future__ import annotations\nfrom collections.abc import Callable\nfrom typing import Type, TypeVar\n\nfrom bcsfe imp"
  },
  {
    "path": "src/bcsfe/core/server/game_data_getter.py",
    "chars": 11964,
    "preview": "from __future__ import annotations\nfrom io import BytesIO\nfrom typing import Any, Callable\n\nfrom bcsfe.cli import color,"
  },
  {
    "path": "src/bcsfe/core/server/headers.py",
    "chars": 952,
    "preview": "from __future__ import annotations\nimport time\nfrom bcsfe import core\n\n\nclass AccountHeaders:\n    def __init__(self, sav"
  },
  {
    "path": "src/bcsfe/core/server/managed_item.py",
    "chars": 6215,
    "preview": "from __future__ import annotations\n\n\"\"\"ManagedItem class for bcsfe.\"\"\"\n\nfrom enum import Enum\nfrom typing import Any\nimp"
  },
  {
    "path": "src/bcsfe/core/server/request.py",
    "chars": 3447,
    "preview": "from __future__ import annotations\n\nimport requests\n\nfrom bcsfe import core\n\n\nclass MultiPartFile:\n    def __init__(self"
  },
  {
    "path": "src/bcsfe/core/server/server_handler.py",
    "chars": 23130,
    "preview": "from __future__ import annotations\nimport base64\nimport time\nfrom typing import Any\n\nfrom bcsfe import core\nimport jwt\n\n"
  },
  {
    "path": "src/bcsfe/core/server/updater.py",
    "chars": 2772,
    "preview": "from __future__ import annotations\nimport sys\nfrom typing import Any\nfrom bcsfe import core\nimport bcsfe\n\n\nclass Updater"
  },
  {
    "path": "src/bcsfe/core/theme_handler.py",
    "chars": 9382,
    "preview": "from __future__ import annotations\nimport dataclasses\nimport tempfile\nfrom typing import Any\nfrom bcsfe import core\nfrom"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/config.properties",
    "chars": 4499,
    "preview": "config=Config\nedit_config=Edit config\ndefault_value=(default value: <@q>{default_value}</>)\ncurrent_value=(current value"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/files.properties",
    "chars": 411,
    "preview": "another_path=Enter path manually\nselect_files_dir=Select files in directory:\nenter_path=Enter file path / location:\nente"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/input.properties",
    "chars": 1611,
    "preview": "input_int=Input a number between <@q>{min}</> and <@q>{max}</>:\nselect_edit=Select options for <@t>{group_name}</>:\ninpu"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/locale.properties",
    "chars": 1727,
    "preview": "available_locales=Available languages:\nlocale_desc=Language to use {{config_value_txt}}\nlocale=Language\nlocale_dialog=Se"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/main.properties",
    "chars": 5282,
    "preview": "# Full documentation: https://codeberg.org/fieryhenry/ExampleEditorLocale#format-of-the-properties-files\n# color formatt"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/save.properties",
    "chars": 7184,
    "preview": "save_load_option=Select an option to load the save file\ndownload_save=Download save file using transfer and confirmation"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/server.properties",
    "chars": 3325,
    "preview": "transfer_code=Transfer Code\nenter_transfer_code=Enter Transfer Code:\nconfirmation_code=Confirmation Code\nenter_confirmat"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/theme.properties",
    "chars": 1279,
    "preview": "theme_text=\n>Current Theme: <@s>{theme_name}</> (Version <@s>{theme_version}</>)\n>Made by <@s>{theme_author}</>\n>Theme F"
  },
  {
    "path": "src/bcsfe/files/locales/en/core/updater.properties",
    "chars": 647,
    "preview": "local_version=<@q>Local version: <@s>{local_version}</></>\nlatest_version=<@q>Latest version: <@s>{latest_version}</></>"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/bannable_items.properties",
    "chars": 1734,
    "preview": "do_you_want_to_continue=Do you want to continue? ({{y/n}}):\n\ncatfood_warning=<@w>WARNING: Editing in cat food can result"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/cats.properties",
    "chars": 8037,
    "preview": "total_selected_cats=<@t>{total}</> cats currently selected\nselected_cat=<@t>{name}</> (<@t>{id}</>) is selected\n\ncat=<@t"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/enemy.properties",
    "chars": 983,
    "preview": "total_selected_enemies=<@t>{total}</> enemies currently selected\nunlock_enemy_guide_success=<@su>Successfully unlocked e"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/fixes.properties",
    "chars": 567,
    "preview": "fix_gamatoto_crash=Fix gamatoto from crashing the game\nfix_time_errors=Fix time related issues\n\nfix_ototo_crash=Fix otot"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/gambling.properties",
    "chars": 198,
    "preview": "reset_wildcat_slots=<@su>Successfully reset wildcat slots</>\nreset_cat_scratcher=<@su>Successfully reset cat scratcher l"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/gamototo.properties",
    "chars": 2035,
    "preview": "enter_raw_gamatoto_xp=Enter Raw Gamatoto XP\nenter_gamatoto_level=Enter Gamatoto Level\nedit_gamatoto_level_q=Enter an opt"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/gatya.properties",
    "chars": 434,
    "preview": "event_tickets=Event Tickets / Lucky Tickets\ndownloading_gatya_data=Downloading gacha event data...\ndownload_gatya_data_s"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/gold_pass.properties",
    "chars": 616,
    "preview": "gold_pass_dialog=Enter the <@t>officer id</> you want (Leave <@q>blank</> for a <@q>random</> id, or enter <@q>-1</> to "
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/items.properties",
    "chars": 2534,
    "preview": "# Note that not all items are here\n\ncatamins=Catamins\ncatfruit=Catfruit\nbase_materials=Base Materials\ninquiry_code=Inqui"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/map.properties",
    "chars": 8704,
    "preview": "tutorial_already_cleared=<@w>You have already cleared the tutorial</>\ntutorial_cleared=<@su>Succesfully cleared tutorial"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/medals.properties",
    "chars": 332,
    "preview": "medals=Meow Medals\nadd_medals=Add Medals\nremove_medals=Remove Medals\nmedal_add_remove_dialog=Do you want to <@t>add meda"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/missions.properties",
    "chars": 508,
    "preview": "missions=Catnip Challenges / Missions|Cat Missions\ncomplete_reward=Clear Missions and Don't Claim Rewards\ncomplete_claim"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/playtime.properties",
    "chars": 601,
    "preview": "playtime_str=<@t>{hours}</> $(hours: !=1($hours)$, hour)/$, <@t>{minutes}</> $(minutes: !=1($minutes)$, minute)/$, <@t>{"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/scheme_items.properties",
    "chars": 335,
    "preview": "scheme_items_edit_success=<@su>Succesfully edited scheme items</>\nscheme_items_select_gain=Select scheme items to gain\ns"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/special_skills.properties",
    "chars": 524,
    "preview": "special_skills_dialog=Select a base ability to upgrade\nupgrade_individual_skill=Input an upgrade for each selected skill"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/talent_orbs.properties",
    "chars": 1235,
    "preview": "total_current_orbs=Total Current Orbs: <@q>{total_orbs}</>\ntotal_current_orb_types=Total Current Orb Types: <@q>{total_t"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/treasures.properties",
    "chars": 1480,
    "preview": "whole_chapters=Whole Chapters\nindividual_stages=Individual Stages\ntreasure_groups=Treasure Groups / Sets\ntreasure_dialog"
  },
  {
    "path": "src/bcsfe/files/locales/en/edits/user_rank.properties",
    "chars": 510,
    "preview": "claim=Claim\nunclaim=Unclaim\nfix_claimed=Fix Claimed\nclaim_or_unclaim_ur=Do you want to <@t>claim</> or <@t>unclaim</> or"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/config.properties",
    "chars": 2808,
    "preview": "config=設定\nedit_config=編輯設定\ndefault_value=(預設值: <@q>{default_value}</>)\ncurrent_value=(目前數值: <@q>{current_value}</>)\nconf"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/files.properties",
    "chars": 262,
    "preview": "another_path=手動輸入路徑\nselect_files_dir=選擇目錄中的檔案:\nenter_path=輸入檔案路徑或位置:\nenter_path_dir=輸入資料夾路徑或位置:\nenter_path_default=輸入檔案路"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/input.properties",
    "chars": 1085,
    "preview": "input_int=輸入 <@q>{min}</> 到 <@q>{max}</>之間的數值:\nselect_edit=為 <@t>{group_name}</>選擇選項:\ninput_int_default=輸入 <@q>{min}</> "
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/locale.properties",
    "chars": 1227,
    "preview": "available_locales=可用的語言:\nlocale_desc=要使用的語言 {{config_value_txt}}\nlocale=語言\nlocale_dialog=選擇語言:\nadd_locale=新增語系\nremove_lo"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/main.properties",
    "chars": 3716,
    "preview": "# Full documentation: https://codeberg.org/fieryhenry/ExampleEditorLocale#format-of-the-properties-files\n# color formatt"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/save.properties",
    "chars": 4365,
    "preview": "save_load_option=選擇載入存檔的方式\ndownload_save=使用轉移碼和認證碼下載存檔\nselect_save_file=從檔案中選擇存檔\nadb_pull_save=使用 ADB 從裝置提取存檔\nwaydroid_p"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/server.properties",
    "chars": 2141,
    "preview": "transfer_code=轉移碼\nenter_transfer_code=輸入轉移碼:\nconfirmation_code=認證碼\nenter_confirmation_code=輸入認證碼:\ncountry_code=國家代碼\ncoun"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/theme.properties",
    "chars": 947,
    "preview": "theme_text=\n>目前主題: <@s>{theme_name}</> (版本 <@s>{theme_version}</>)\n>作者 <@s>{theme_author}</>\n>主題檔案位置: <@s>{theme_path}</"
  },
  {
    "path": "src/bcsfe/files/locales/tw/core/updater.properties",
    "chars": 435,
    "preview": "local_version=<@q>本機版本:<@s>{local_version}</></>\nlatest_version=<@q>最新版本:<@s>{latest_version}</></>\n\nupdate_check_fail=<"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/bannable_items.properties",
    "chars": 1004,
    "preview": "do_you_want_to_continue=是否要繼續? ({{y/n}}):\n\ncatfood_warning=<@w>警告:修改貓罐頭可能會導致被鎖帳。請自行承擔風險。</>\\n{{do_you_want_to_continue}}"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/cats.properties",
    "chars": 5274,
    "preview": "total_selected_cats=目前已選取 <@t>{total}</> 隻貓咪\nselected_cat=已選取 <@t>{name}</> (<@t>{id}</>)\n\ncat=<@t>{name}</> (<@t>{id}</"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/enemy.properties",
    "chars": 662,
    "preview": "total_selected_enemies=目前已選取 <@t>{total}</> 種敵人\nunlock_enemy_guide_success=<@su>已成功解鎖敵人圖鑑項目</>\nremove_enemy_guide_succes"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/fixes.properties",
    "chars": 345,
    "preview": "fix_gamatoto_crash=修復加碼多多造成的遊戲閃退\nfix_time_errors=修復時間相關錯誤\n\nfix_ototo_crash=修復城堡寶開發隊造成的遊戲閃退\n\nfix_gamatoto_crash_success=<"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/gambling.properties",
    "chars": 114,
    "preview": "reset_wildcat_slots=<@su>已成功重置貓咪拉霸機</>\nreset_cat_scratcher=<@su>已成功重置貓咪刮刮樂</>\nreset_gambling_events=重置貓咪拉霸機與貓咪刮刮樂\n"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/gamototo.properties",
    "chars": 1476,
    "preview": "enter_raw_gamatoto_xp=輸入加碼多多經驗值\nenter_gamatoto_level=輸入加碼多多等級\nedit_gamatoto_level_q=輸入選項以編輯加碼多多等級:\ngamatoto_xp=加碼多多經驗值\ng"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/gatya.properties",
    "chars": 282,
    "preview": "event_tickets=活動轉蛋券 / 招福轉蛋券\ndownloading_gatya_data=正在下載轉蛋活動資料...\ndownload_gatya_data_success=<@su>已成功下載轉蛋活動資料</>\ndownloa"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/gold_pass.properties",
    "chars": 376,
    "preview": "gold_pass_dialog=輸入你想要的 <@t>會員 ID</>(留 <@q>白</> 則產生 <@q>隨機</> ID,或者輸入 <@q>-1</> 來 <@q>移除</> 黃金會員):\ngold_pass=黃金會員 / 貓咪俱樂"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/items.properties",
    "chars": 1683,
    "preview": "# Note that not all items are here\n\ncatamins=喵力達\ncatfruit=貓薄荷\nbase_materials=城堡開發材料\ninquiry_code=詢問碼\nrare_gatya_seed=稀有轉"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/map.properties",
    "chars": 5300,
    "preview": "tutorial_already_cleared=<@w>你已經通過教學關卡</>\ntutorial_cleared=<@su>已成功通過教學關卡</>\nclear_tutorial=通過教學關卡\n\nclear_stages=通關關卡\nun"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/medals.properties",
    "chars": 230,
    "preview": "medals=貓咪獎章\nadd_medals=新增獎章\nremove_medals=移除獎章\nmedal_add_remove_dialog=你希望 <@t>新增獎章</> 或 <@t>移除獎章</>?:\nmedal_string={med"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/missions.properties",
    "chars": 244,
    "preview": "missions=貓咪任務\ncomplete_reward=完成任務但不領取獎勵\ncomplete_claim=完成任務並領取獎勵\nuncomplete=取消完成任務\nselect_mission_claim=你要<@t>完成任務但不領取獎"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/playtime.properties",
    "chars": 327,
    "preview": "playtime_str=<@t>{hours}</> 小時, <@t>{minutes}</> 分鐘, <@t>{seconds}</> 秒 (<@t>{frames} </>幀)\nplaytime_current=目前的遊戲時數:{{p"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/scheme_items.properties",
    "chars": 236,
    "preview": "scheme_items_edit_success=<@su>已成功編輯獸石/獸結晶</>\nscheme_items_select_gain=選擇要獲取的獸石/獸結晶\nscheme_items_select_remove=選擇要移除的獸石/"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/special_skills.properties",
    "chars": 377,
    "preview": "special_skills_dialog=選擇要升級的基地能力\nupgrade_individual_skill=為每個選取的能力個別輸入等級\nupgrade_all_skills=輸入等級數值以套用至所有選取的能力\n\nupgrade_s"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/talent_orbs.properties",
    "chars": 796,
    "preview": "total_current_orbs=目前本能玉總數:<@q>{total_orbs}</>\ntotal_current_orb_types=目前本能玉種類總數:<@q>{total_types}</>\ncurrent_orbs=目前的本能"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/treasures.properties",
    "chars": 909,
    "preview": "whole_chapters=整個章節\nindividual_stages=個別關卡\ntreasure_groups=寶物群組 / 寶物區域\ntreasure_dialog=你要編輯<@t>整個章節</>、<@t>個別關卡</>還是個別<@"
  },
  {
    "path": "src/bcsfe/files/locales/tw/edits/user_rank.properties",
    "chars": 316,
    "preview": "claim=領取\nunclaim=取消領取\nfix_claimed=修復領取狀態\nclaim_or_unclaim_ur=你想要<@t>領取</>、<@t>取消領取</>還是<@t>修復領取狀態 (取消領取任何高於目前等級排行的獎勵)</>"
  },
  {
    "path": "src/bcsfe/files/locales/tw/metadata.json",
    "chars": 69,
    "preview": "{\n  \"name\": \"繁體中文 (Traditional Chinese)\",\n  \"authors\": [\"LinYuAn\"]\n}\n"
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/config.properties",
    "chars": 4690,
    "preview": "# filename=\"config.properties\"\r\nconfig=Cấu hình\r\nedit_config=Chỉnh sửa cấu hình\r\ndefault_value=(giá trị mặc định: <@q>{d"
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/files.properties",
    "chars": 466,
    "preview": "# filename=\"files.properties\"\r\nanother_path=Nhập đường dẫn thủ công\r\nselect_files_dir=Chọn tệp trong thư mục:\r\nenter_pat"
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/input.properties",
    "chars": 1688,
    "preview": "# filename=\"input.properties\"\r\ninput_int=Nhập một số giữa <@q>{min}</> và <@q>{max}</>:\r\nselect_edit=Chọn các tùy chọn c"
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/locale.properties",
    "chars": 1813,
    "preview": "# filename=\"locale.properties\"\r\navailable_locales=Các ngôn ngữ khả dụng:\r\nlocale_desc=Ngôn ngữ sử dụng {{config_value_tx"
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/main.properties",
    "chars": 5308,
    "preview": "# filename=\"main.properties\"\r\n# Full documentation: https://codeberg.org/fieryhenry/ExampleEditorLocale#format-of-the-pr"
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/save.properties",
    "chars": 6460,
    "preview": "# filename=\"save.properties\"\r\nsave_load_option=Chọn một tùy chọn để tải save file\r\ndownload_save=Tải xuống save file sử "
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/server.properties",
    "chars": 2979,
    "preview": "# filename=\"server.properties\"\r\ntransfer_code=Transfer Code\r\nenter_transfer_code=Nhập Transfer Code:\r\nconfirmation_code="
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/theme.properties",
    "chars": 1355,
    "preview": "# filename=\"theme.properties\"\r\ntheme_text=\r\n>Chủ đề hiện tại: <@s>{theme_name}</> (Phiên bản <@s>{theme_version}</>)\r\n>Đ"
  },
  {
    "path": "src/bcsfe/files/locales/vi/core/updater.properties",
    "chars": 724,
    "preview": "# filename=\"updater.properties\"\r\nlocal_version=<@q>Phiên bản cục bộ: <@s>{local_version}</></>\r\nlatest_version=<@q>Phiên"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/bannable_items.properties",
    "chars": 1880,
    "preview": "# filename=\"bannable_items.properties\"\r\ndo_you_want_to_continue=Bạn có muốn tiếp tục không? ({{y/n}}):\r\n\r\ncatfood_warnin"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/cats.properties",
    "chars": 8167,
    "preview": "# filename=\"cats.properties\"\r\ntotal_selected_cats=<@t>{total}</> cats hiện đang được chọn\r\nselected_cat=<@t>{name}</> (<"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/enemy.properties",
    "chars": 1034,
    "preview": "# filename=\"enemy.properties\"\r\ntotal_selected_enemies=<@t>{total}</> enemies hiện đang được chọn\r\nunlock_enemy_guide_suc"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/fixes.properties",
    "chars": 777,
    "preview": "# filename=\"fixes.properties\"\r\nfix_gamatoto_crash=Fix gamatoto gây crashing game\r\nfix_time_errors=Fix vấn đề liên quan đ"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/gambling.properties",
    "chars": 199,
    "preview": "reset_wildcat_slots=<@su>Đã reset Wildcat Slots thành công</>\nreset_gambling_events=Reset Wildcat Slots và Cat Scratcher"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/gamototo.properties",
    "chars": 2071,
    "preview": "# filename=\"gamototo.properties\"\r\nenter_raw_gamatoto_xp=Nhập Raw Gamatoto XP\r\nenter_gamatoto_level=Nhập Gamatoto Level\r\n"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/gatya.properties",
    "chars": 470,
    "preview": "# filename=\"gatya.properties\"\r\nevent_tickets=Event Tickets / Lucky Tickets\r\ndownloading_gatya_data=Đang tải dữ liệu sự k"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/gold_pass.properties",
    "chars": 470,
    "preview": "# filename=\"gold_pass.properties\"\r\ngold_pass_dialog=Nhập <@t>officer id</> bạn muốn (Để <@q>trống</> cho <@q>random</> i"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/items.properties",
    "chars": 1989,
    "preview": "# filename=\"items.properties\"\r\n# Lưu ý rằng không phải tất cả các vật phẩm đều có ở đây\r\n\r\ncatamins=Catamins\r\ncatfruit=C"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/map.properties",
    "chars": 7047,
    "preview": "# filename=\"map.properties\"\r\ntutorial_already_cleared=<@w>Bạn đã clear tutorial</>\r\ntutorial_cleared=<@su>Đã clear tutor"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/medals.properties",
    "chars": 362,
    "preview": "# filename=\"medals.properties\"\r\nmedals=Meow Medals\r\nadd_medals=Thêm Medals\r\nremove_medals=Xóa Medals\r\nmedal_add_remove_d"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/missions.properties",
    "chars": 574,
    "preview": "# filename=\"missions.properties\"\r\nmissions=Catnip Challenges / Missions|Cat Missions\r\ncomplete_reward=Hoàn thành Mission"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/playtime.properties",
    "chars": 518,
    "preview": "# filename=\"playtime.properties\"\r\nplaytime_str=<@t>{hours}</> giờ, <@t>{minutes}</> phút, <@t>{seconds}</> giây (<@t>{fr"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/scheme_items.properties",
    "chars": 365,
    "preview": "# filename=\"scheme_items.properties\"\r\nscheme_items_edit_success=<@su>Đã chỉnh sửa scheme items thành công</>\r\nscheme_ite"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/special_skills.properties",
    "chars": 580,
    "preview": "# filename=\"special_skills.properties\"\r\nspecial_skills_dialog=Chọn một base ability để nâng cấp\r\nupgrade_individual_skil"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/talent_orbs.properties",
    "chars": 1290,
    "preview": "# filename=\"talent_orbs.properties\"\r\ntotal_current_orbs=Tổng Orbs Hiện Tại: <@q>{total_orbs}</>\r\ntotal_current_orb_types"
  },
  {
    "path": "src/bcsfe/files/locales/vi/edits/treasures.properties",
    "chars": 1517,
    "preview": "# filename=\"treasures.properties\"\r\nwhole_chapters=Toàn bộ chapters\r\nindividual_stages=Từng stages riêng lẻ\r\ntreasure_gro"
  }
]

// ... and 8 more files (download for full content)

About this extraction

This page contains the full source code of the fieryhenry/BCSFE-Python GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 208 files (1.1 MB), approximately 297.3k tokens, and a symbol index with 2892 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!