silkedit 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rbcli +0 -0
- data/CHANGELOG.md +7 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +169 -0
- data/README.md +302 -0
- data/Rakefile +8 -0
- data/exe/silkedit +13 -0
- data/lib/commands/backup_restore.rb +25 -0
- data/lib/commands/cheat.rb +74 -0
- data/lib/commands/edit_diff.rb +47 -0
- data/lib/commands/examples/command.rb +51 -0
- data/lib/commands/examples/script.rb +36 -0
- data/lib/commands/journal.rb +34 -0
- data/lib/commands/permasave_permaload.rb +62 -0
- data/lib/commands/scripts/.keep +0 -0
- data/lib/commands/unpack_repack.rb +27 -0
- data/lib/commands/zone_mkzone.rb +103 -0
- data/lib/config/config.rb +28 -0
- data/lib/config/envvars.rb +23 -0
- data/lib/config/hooks.rb +22 -0
- data/lib/config/logger.rb +36 -0
- data/lib/config/parser.rb +34 -0
- data/lib/config/updatechecker.rb +12 -0
- data/lib/silkedit/cheats/cheatengine.rb +33 -0
- data/lib/silkedit/cheats/hollow_knight_cheats.rb +5 -0
- data/lib/silkedit/cheats/merger.rb +107 -0
- data/lib/silkedit/cheats/silksong_cheats.rb +161 -0
- data/lib/silkedit/cheats/silksong_enemyjournal.rb +120 -0
- data/lib/silkedit/cheats/silksong_zoner.rb +98 -0
- data/lib/silkedit/config/savegame.yaml +13 -0
- data/lib/silkedit/config/silkedit.yaml +6 -0
- data/lib/silkedit/config/silksong/cheatdata.yaml +1341 -0
- data/lib/silkedit/config/silksong/enemylist.yaml +1424 -0
- data/lib/silkedit/config/silksong/old.yaml +4345 -0
- data/lib/silkedit/config/silksong/permasaves.yaml +12 -0
- data/lib/silkedit/config/silksong/unfinished.yaml +628 -0
- data/lib/silkedit/config/silksong/zones.yaml +1738 -0
- data/lib/silkedit/images/.keep +0 -0
- data/lib/silkedit/images/silksong/Aknid.png +0 -0
- data/lib/silkedit/images/silksong/Alita.png +0 -0
- data/lib/silkedit/images/silksong/Barnak.png +0 -0
- data/lib/silkedit/images/silksong/Beastfly.png +0 -0
- data/lib/silkedit/images/silksong/Bell_Beast.png +0 -0
- data/lib/silkedit/images/silksong/Bell_Eater.png +0 -0
- data/lib/silkedit/images/silksong/Bloatroach.png +0 -0
- data/lib/silkedit/images/silksong/Broodmother.png +0 -0
- data/lib/silkedit/images/silksong/Brushflit.png +0 -0
- data/lib/silkedit/images/silksong/Burning_Bug.png +0 -0
- data/lib/silkedit/images/silksong/Caranid.png +0 -0
- data/lib/silkedit/images/silksong/Choir_Bellbearer.png +0 -0
- data/lib/silkedit/images/silksong/Choir_Clapper.png +0 -0
- data/lib/silkedit/images/silksong/Choir_Elder.png +0 -0
- data/lib/silkedit/images/silksong/Choir_Flyer.png +0 -0
- data/lib/silkedit/images/silksong/Choir_Hornhead.png +0 -0
- data/lib/silkedit/images/silksong/Choir_Pouncer.png +0 -0
- data/lib/silkedit/images/silksong/Choristor.png +0 -0
- data/lib/silkedit/images/silksong/Clawmaiden.png +0 -0
- data/lib/silkedit/images/silksong/Clover_Dancers.png +0 -0
- data/lib/silkedit/images/silksong/Cloverstag.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Choirbug.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Clapper.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Cleanser.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Crawler.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Dancers.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Defender.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Hauler.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Spine.png +0 -0
- data/lib/silkedit/images/silksong/Cogwork_Underfly.png +0 -0
- data/lib/silkedit/images/silksong/Cogworker.png +0 -0
- data/lib/silkedit/images/silksong/Conchfly.png +0 -0
- data/lib/silkedit/images/silksong/Coral_Furm.png +0 -0
- data/lib/silkedit/images/silksong/Corrcrust_Karaka.png +0 -0
- data/lib/silkedit/images/silksong/Covetous_Pilgrim.png +0 -0
- data/lib/silkedit/images/silksong/Craggler.png +0 -0
- data/lib/silkedit/images/silksong/Cragglite.png +0 -0
- data/lib/silkedit/images/silksong/Craw.png +0 -0
- data/lib/silkedit/images/silksong/Craw_Juror.png +0 -0
- data/lib/silkedit/images/silksong/Crawfather.png +0 -0
- data/lib/silkedit/images/silksong/Crust_King_Khann.png +0 -0
- data/lib/silkedit/images/silksong/Crustcrag.png +0 -0
- data/lib/silkedit/images/silksong/Crustcrawler.png +0 -0
- data/lib/silkedit/images/silksong/Deep_Diver.png +0 -0
- data/lib/silkedit/images/silksong/Disgraced_Chef_Lugoli.png +0 -0
- data/lib/silkedit/images/silksong/Drapefly.png +0 -0
- data/lib/silkedit/images/silksong/Drapelord.png +0 -0
- data/lib/silkedit/images/silksong/Drapemite.png +0 -0
- data/lib/silkedit/images/silksong/Dreg_Catcher.png +0 -0
- data/lib/silkedit/images/silksong/Dreg_Husk.png +0 -0
- data/lib/silkedit/images/silksong/Dregwheel.png +0 -0
- data/lib/silkedit/images/silksong/Driftlin.png +0 -0
- data/lib/silkedit/images/silksong/Driznarga.png +0 -0
- data/lib/silkedit/images/silksong/Driznit.png +0 -0
- data/lib/silkedit/images/silksong/Ductsucker.png +0 -0
- data/lib/silkedit/images/silksong/Elder_Pilgrim.png +0 -0
- data/lib/silkedit/images/silksong/Envoy.png +0 -0
- data/lib/silkedit/images/silksong/Escalion.png +0 -0
- data/lib/silkedit/images/silksong/Father_of_the_Flame.png +0 -0
- data/lib/silkedit/images/silksong/Fertid.png +0 -0
- data/lib/silkedit/images/silksong/First_Sinner.png +0 -0
- data/lib/silkedit/images/silksong/Flapping_Fertid.png +0 -0
- data/lib/silkedit/images/silksong/Flintbeetle.png +0 -0
- data/lib/silkedit/images/silksong/Flintflame_Flyer.png +0 -0
- data/lib/silkedit/images/silksong/Flintstone_Flyer.png +0 -0
- data/lib/silkedit/images/silksong/Fluttermite.png +0 -0
- data/lib/silkedit/images/silksong/Forebrothers_Signis_&_Gron.png +0 -0
- data/lib/silkedit/images/silksong/Fourth_Chorus.png +0 -0
- data/lib/silkedit/images/silksong/Freshfly.png +0 -0
- data/lib/silkedit/images/silksong/Furm.png +0 -0
- data/lib/silkedit/images/silksong/Gahlia.png +0 -0
- data/lib/silkedit/images/silksong/Gargant_Gloom.png +0 -0
- data/lib/silkedit/images/silksong/Garpid.png +0 -0
- data/lib/silkedit/images/silksong/Giant_Drapemite.png +0 -0
- data/lib/silkedit/images/silksong/Gloomsac.png +0 -0
- data/lib/silkedit/images/silksong/Grand_Mother_Silk.png +0 -0
- data/lib/silkedit/images/silksong/Grand_Reed.png +0 -0
- data/lib/silkedit/images/silksong/Great_Conchfly.png +0 -0
- data/lib/silkedit/images/silksong/Groal_the_Great.png +0 -0
- data/lib/silkedit/images/silksong/Grom.png +0 -0
- data/lib/silkedit/images/silksong/Gromling.png +0 -0
- data/lib/silkedit/images/silksong/Guardfly.png +0 -0
- data/lib/silkedit/images/silksong/Gurr_the_Outcast.png +0 -0
- data/lib/silkedit/images/silksong/Hardbone_Elder.png +0 -0
- data/lib/silkedit/images/silksong/Hardbone_Hopper.png +0 -0
- data/lib/silkedit/images/silksong/Hoker.png +0 -0
- data/lib/silkedit/images/silksong/Huge_Flea.png +0 -0
- data/lib/silkedit/images/silksong/Imoba.png +0 -0
- data/lib/silkedit/images/silksong/Judge.png +0 -0
- data/lib/silkedit/images/silksong/Kai.png +0 -0
- data/lib/silkedit/images/silksong/Kakri.png +0 -0
- data/lib/silkedit/images/silksong/Karak_Gor.png +0 -0
- data/lib/silkedit/images/silksong/Karaka.png +0 -0
- data/lib/silkedit/images/silksong/Kilik.png +0 -0
- data/lib/silkedit/images/silksong/Kindanir.png +0 -0
- data/lib/silkedit/images/silksong/Lace.png +0 -0
- data/lib/silkedit/images/silksong/Lampbearer.png +0 -0
- data/lib/silkedit/images/silksong/Last_Claw.png +0 -0
- data/lib/silkedit/images/silksong/Last_Judge.png +0 -0
- data/lib/silkedit/images/silksong/Lavalarga.png +0 -0
- data/lib/silkedit/images/silksong/Lavalug.png +0 -0
- data/lib/silkedit/images/silksong/Leaf_Glider.png +0 -0
- data/lib/silkedit/images/silksong/Leaf_Roller.png +0 -0
- data/lib/silkedit/images/silksong/Lost_Garmond.png +0 -0
- data/lib/silkedit/images/silksong/Lost_Lace.png +0 -0
- data/lib/silkedit/images/silksong/Maestro.png +0 -0
- data/lib/silkedit/images/silksong/Marrowmaw.png +0 -0
- data/lib/silkedit/images/silksong/Massive_Mossgrub.png +0 -0
- data/lib/silkedit/images/silksong/Mawling.png +0 -0
- data/lib/silkedit/images/silksong/Memoria.png +0 -0
- data/lib/silkedit/images/silksong/Minister.png +0 -0
- data/lib/silkedit/images/silksong/Miremite.png +0 -0
- data/lib/silkedit/images/silksong/Mite.png +0 -0
- data/lib/silkedit/images/silksong/Mitemother.png +0 -0
- data/lib/silkedit/images/silksong/Mnemonid.png +0 -0
- data/lib/silkedit/images/silksong/Mnemonord.png +0 -0
- data/lib/silkedit/images/silksong/Moorwing.png +0 -0
- data/lib/silkedit/images/silksong/Mortician.png +0 -0
- data/lib/silkedit/images/silksong/Moss_Mother.png +0 -0
- data/lib/silkedit/images/silksong/Mossgrub.png +0 -0
- data/lib/silkedit/images/silksong/Mossmir.png +0 -0
- data/lib/silkedit/images/silksong/Mothleaf_Lagnia.png +0 -0
- data/lib/silkedit/images/silksong/Muckmaggot.png +0 -0
- data/lib/silkedit/images/silksong/Muckroach.png +0 -0
- data/lib/silkedit/images/silksong/Nuphar.png +0 -0
- data/lib/silkedit/images/silksong/Nyleth.png +0 -0
- data/lib/silkedit/images/silksong/Overgrown_Pilgrim.png +0 -0
- data/lib/silkedit/images/silksong/Palestag.png +0 -0
- data/lib/silkedit/images/silksong/Pendra.png +0 -0
- data/lib/silkedit/images/silksong/Pendragor.png +0 -0
- data/lib/silkedit/images/silksong/Penitent.png +0 -0
- data/lib/silkedit/images/silksong/Phacia.png +0 -0
- data/lib/silkedit/images/silksong/Phantom.png +0 -0
- data/lib/silkedit/images/silksong/Pharlid.png +0 -0
- data/lib/silkedit/images/silksong/Pharlid_Diver.png +0 -0
- data/lib/silkedit/images/silksong/Pilgrim_Bellbearer.png +0 -0
- data/lib/silkedit/images/silksong/Pilgrim_Groveller.png +0 -0
- data/lib/silkedit/images/silksong/Pilgrim_Guide.png +0 -0
- data/lib/silkedit/images/silksong/Pilgrim_Hiker.png +0 -0
- data/lib/silkedit/images/silksong/Pilgrim_Hornfly.png +0 -0
- data/lib/silkedit/images/silksong/Pilgrim_Hulk.png +0 -0
- data/lib/silkedit/images/silksong/Pilgrim_Pouncer.png +0 -0
- data/lib/silkedit/images/silksong/Pinstress.png +0 -0
- data/lib/silkedit/images/silksong/Plasmid.png +0 -0
- data/lib/silkedit/images/silksong/Plasmidas.png +0 -0
- data/lib/silkedit/images/silksong/Plasmified_Zango.png +0 -0
- data/lib/silkedit/images/silksong/Pokenabbin.png +0 -0
- data/lib/silkedit/images/silksong/Pollenica.png +0 -0
- data/lib/silkedit/images/silksong/Pond_Skipper.png +0 -0
- data/lib/silkedit/images/silksong/Pondcatcher.png +0 -0
- data/lib/silkedit/images/silksong/Puny_Penitent.png +0 -0
- data/lib/silkedit/images/silksong/Reed.png +0 -0
- data/lib/silkedit/images/silksong/Rhinogrund.png +0 -0
- data/lib/silkedit/images/silksong/Roachcatcher.png +0 -0
- data/lib/silkedit/images/silksong/Roachfeeder.png +0 -0
- data/lib/silkedit/images/silksong/Roachkeeper.png +0 -0
- data/lib/silkedit/images/silksong/Roachserver.png +0 -0
- data/lib/silkedit/images/silksong/Sandcarver.png +0 -0
- data/lib/silkedit/images/silksong/Savage_Beastfly.png +0 -0
- data/lib/silkedit/images/silksong/Scabfly.png +0 -0
- data/lib/silkedit/images/silksong/Scrollreader.png +0 -0
- data/lib/silkedit/images/silksong/Second_Sentinel.png +0 -0
- data/lib/silkedit/images/silksong/Servitor_Boran.png +0 -0
- data/lib/silkedit/images/silksong/Servitor_Ignim.png +0 -0
- data/lib/silkedit/images/silksong/Shadow_Charger.png +0 -0
- data/lib/silkedit/images/silksong/Shadow_Creeper_(Silksong).png +0 -0
- data/lib/silkedit/images/silksong/Shardillard.png +0 -0
- data/lib/silkedit/images/silksong/Shellwood_Gnat.png +0 -0
- data/lib/silkedit/images/silksong/Shrine_Guardian_Seth.png +0 -0
- data/lib/silkedit/images/silksong/Silk_Snipper.png +0 -0
- data/lib/silkedit/images/silksong/Sister_Splinter.png +0 -0
- data/lib/silkedit/images/silksong/Skarr_Scout.png +0 -0
- data/lib/silkedit/images/silksong/Skarr_Stalker.png +0 -0
- data/lib/silkedit/images/silksong/Skarrgard.png +0 -0
- data/lib/silkedit/images/silksong/Skarrlid.png +0 -0
- data/lib/silkedit/images/silksong/Skarrsinger_Karmelita.png +0 -0
- data/lib/silkedit/images/silksong/Skarrwing.png +0 -0
- data/lib/silkedit/images/silksong/Skrill.png +0 -0
- data/lib/silkedit/images/silksong/Skull_Brute.png +0 -0
- data/lib/silkedit/images/silksong/Skull_Scuttler.png +0 -0
- data/lib/silkedit/images/silksong/Skull_Tyrant.png +0 -0
- data/lib/silkedit/images/silksong/Skullwing.png +0 -0
- data/lib/silkedit/images/silksong/Slubberlug.png +0 -0
- data/lib/silkedit/images/silksong/Smelt_Shoveller.png +0 -0
- data/lib/silkedit/images/silksong/Smokerock_Sifter.png +0 -0
- data/lib/silkedit/images/silksong/Snitchfly.png +0 -0
- data/lib/silkedit/images/silksong/Spear_Skarr.png +0 -0
- data/lib/silkedit/images/silksong/Spinebeak_Kai.png +0 -0
- data/lib/silkedit/images/silksong/Spit_Squit.png +0 -0
- data/lib/silkedit/images/silksong/Splinter.png +0 -0
- data/lib/silkedit/images/silksong/Splinterbark.png +0 -0
- data/lib/silkedit/images/silksong/Splinterhorn.png +0 -0
- data/lib/silkedit/images/silksong/Squatcraw.png +0 -0
- data/lib/silkedit/images/silksong/Squatcraw_Juror.png +0 -0
- data/lib/silkedit/images/silksong/Squirrm.png +0 -0
- data/lib/silkedit/images/silksong/Steelspine_Kai.png +0 -0
- data/lib/silkedit/images/silksong/Stilkin.png +0 -0
- data/lib/silkedit/images/silksong/Stilkin_Trapper.png +0 -0
- data/lib/silkedit/images/silksong/Summoned_Saviour.png +0 -0
- data/lib/silkedit/images/silksong/Surgeon.png +0 -0
- data/lib/silkedit/images/silksong/Swamp_Squit.png +0 -0
- data/lib/silkedit/images/silksong/Tallcraw.png +0 -0
- data/lib/silkedit/images/silksong/Tallcraw_Juror.png +0 -0
- data/lib/silkedit/images/silksong/Tarmite.png +0 -0
- data/lib/silkedit/images/silksong/The_Unravelled.png +0 -0
- data/lib/silkedit/images/silksong/Thread_Raker.png +0 -0
- data/lib/silkedit/images/silksong/Tormented_Trobbio.png +0 -0
- data/lib/silkedit/images/silksong/Trobbio.png +0 -0
- data/lib/silkedit/images/silksong/Undercrank.png +0 -0
- data/lib/silkedit/images/silksong/Underloft.png +0 -0
- data/lib/silkedit/images/silksong/Underpoke.png +0 -0
- data/lib/silkedit/images/silksong/Underscrub.png +0 -0
- data/lib/silkedit/images/silksong/Undersweep.png +0 -0
- data/lib/silkedit/images/silksong/Underworker.png +0 -0
- data/lib/silkedit/images/silksong/Vaultborn.png +0 -0
- data/lib/silkedit/images/silksong/Vaultkeeper.png +0 -0
- data/lib/silkedit/images/silksong/Verdanir.png +0 -0
- data/lib/silkedit/images/silksong/Vicious_Caranid.png +0 -0
- data/lib/silkedit/images/silksong/Void_Mass.png +0 -0
- data/lib/silkedit/images/silksong/Void_Tendrils_(Silksong).png +0 -0
- data/lib/silkedit/images/silksong/Voltvyrm.png +0 -0
- data/lib/silkedit/images/silksong/Wardenfly.png +0 -0
- data/lib/silkedit/images/silksong/Watcher_at_the_Edge.png +0 -0
- data/lib/silkedit/images/silksong/Widow.png +0 -0
- data/lib/silkedit/images/silksong/Winged_Furm.png +0 -0
- data/lib/silkedit/images/silksong/Winged_Lifeseed.png +0 -0
- data/lib/silkedit/images/silksong/Winged_Pilgrim.png +0 -0
- data/lib/silkedit/images/silksong/Winged_Pilgrim_Bellbearer.png +0 -0
- data/lib/silkedit/images/silksong/Wingmould_(Silksong).png +0 -0
- data/lib/silkedit/images/silksong/Wisp.png +0 -0
- data/lib/silkedit/images/silksong/Wood_Wasp.png +0 -0
- data/lib/silkedit/images/silksong/Wraith.png +0 -0
- data/lib/silkedit/images/silksong/Yago.png +0 -0
- data/lib/silkedit/images/silksong/Yuma.png +0 -0
- data/lib/silkedit/images/silksong/Yumama.png +0 -0
- data/lib/silkedit/savegame/crypto.rb +25 -0
- data/lib/silkedit/savegame/diff.rb +159 -0
- data/lib/silkedit/savegame/packer.rb +75 -0
- data/lib/silkedit/savegame/savefile.rb +157 -0
- data/lib/silkedit/util/system.rb +37 -0
- data/lib/silkedit/version.rb +5 -0
- data/lib/silkedit.rb +9 -0
- data/sig/silkedit.rbs +4 -0
- metadata +505 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
|
|
3
|
+
module Silkedit::Savegame
|
|
4
|
+
module Crypto
|
|
5
|
+
AES_KEY = YAML.safe_load_file(File.join(Silkedit::LIBDIR, 'config', 'savegame.yaml'), symbolize_names: true)[:aes_key]
|
|
6
|
+
|
|
7
|
+
def initialize(key)
|
|
8
|
+
@aes_key = key
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.aes_encrypt(data)
|
|
12
|
+
cipher = OpenSSL::Cipher.new('AES-256-ECB')
|
|
13
|
+
cipher.encrypt
|
|
14
|
+
cipher.key = AES_KEY
|
|
15
|
+
cipher.update(data) + cipher.final
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.aes_decrypt(data)
|
|
19
|
+
cipher = OpenSSL::Cipher.new('AES-256-ECB')
|
|
20
|
+
cipher.decrypt
|
|
21
|
+
cipher.key = AES_KEY
|
|
22
|
+
cipher.update(data) + cipher.final
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require 'pp'
|
|
2
|
+
|
|
3
|
+
module Silkedit::Savegame
|
|
4
|
+
module Diff
|
|
5
|
+
def self.mkdiff (old, new)
|
|
6
|
+
return { old: old, new: new } if old.class != new.class
|
|
7
|
+
|
|
8
|
+
diff = nil
|
|
9
|
+
|
|
10
|
+
if old.is_a?(Hash) || new.is_a?(Hash)
|
|
11
|
+
diff = {}
|
|
12
|
+
(old.keys + new.keys).uniq.each do |k|
|
|
13
|
+
itemdiff = mkdiff(old[k], new[k])
|
|
14
|
+
diff[k] = itemdiff unless itemdiff.nil? || itemdiff.empty?
|
|
15
|
+
end
|
|
16
|
+
elsif old.is_a?(Array) || new.is_a?(Array)
|
|
17
|
+
diff = []
|
|
18
|
+
if (old.first.is_a?(Hash) && old.first.key?('Name')) ||
|
|
19
|
+
(new.first.is_a?(Hash) && new.first.key?('Name'))
|
|
20
|
+
keycomps = %w[Name]
|
|
21
|
+
elsif (old.first.is_a?(Hash) && old.first.key?('SceneName') && old.first.key?('ID')) ||
|
|
22
|
+
(new.first.is_a?(Hash) && new.first.key?('SceneName') && new.first.key?('ID'))
|
|
23
|
+
keycomps = %w[SceneName ID]
|
|
24
|
+
elsif (old.first.is_a?(Hash) && old.first.key?('SceneName') && old.first.key?('EventType')) ||
|
|
25
|
+
(new.first.is_a?(Hash) && new.first.key?('SceneName') && new.first.key?('EventType'))
|
|
26
|
+
keycomps = %w[SceneName EventType]
|
|
27
|
+
else
|
|
28
|
+
keycomps = nil
|
|
29
|
+
end
|
|
30
|
+
if keycomps.nil?
|
|
31
|
+
diff = { old: old.reject { |e| new.include?(e) }, new: new.reject { |e| old.include?(e) } }
|
|
32
|
+
diff = nil if diff[:old].empty? && diff[:new].empty?
|
|
33
|
+
else
|
|
34
|
+
old.each do |o|
|
|
35
|
+
n = new.find { |e| keycomps.map { |k| e[k] == o[k] }.reduce { |a, b| a && b } }
|
|
36
|
+
if n.nil?
|
|
37
|
+
diff.append({new: n, old: o})
|
|
38
|
+
next
|
|
39
|
+
end
|
|
40
|
+
itemdiff = mkdiff(
|
|
41
|
+
o.reject { |k, _v| keycomps.include?(k) },
|
|
42
|
+
n.nil? ? nil : n.reject { |k, _v| keycomps.include?(k) }
|
|
43
|
+
)
|
|
44
|
+
next if itemdiff.empty?
|
|
45
|
+
newobj = {}
|
|
46
|
+
keycomps.each { |k| newobj[k] = o[k] }
|
|
47
|
+
diff.append(newobj.merge(itemdiff))
|
|
48
|
+
end
|
|
49
|
+
new.each do |n|
|
|
50
|
+
o = old.find { |e| keycomps.map { |k| e[k] == n[k] }.reduce { |a, b| a && b } }
|
|
51
|
+
next unless o.nil?
|
|
52
|
+
diff.append({new: n, old: o})
|
|
53
|
+
next
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
elsif old != new
|
|
57
|
+
diff = { old: old, new: new }
|
|
58
|
+
end
|
|
59
|
+
return diff
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.parse_diff(obj, yaml_output: false)
|
|
63
|
+
if yaml_output
|
|
64
|
+
sio = StringIO.new
|
|
65
|
+
YAML.dump(obj, sio)
|
|
66
|
+
colored_string = ''
|
|
67
|
+
|
|
68
|
+
sio.string.each_line do |line|
|
|
69
|
+
i = 0
|
|
70
|
+
while i < line.length
|
|
71
|
+
blank_leadup = line[0..i-1].chars.map { |c| c == ' ' }.all?
|
|
72
|
+
if line[i] == '-' && blank_leadup
|
|
73
|
+
colored_string += line[i].colorize(:yellow)
|
|
74
|
+
i += 1
|
|
75
|
+
elsif line[i] == ':' && (blank_leadup || line[0..i-1].sub('-', '').chars.map { |c| c == ' ' }.all?)
|
|
76
|
+
j = i + line[i+1..-1].index(':')
|
|
77
|
+
colored_string += line[i..j].colorize(:magenta)
|
|
78
|
+
i += j - i + 1
|
|
79
|
+
elsif line[i].match(%r{[\"\w]}) && line[i+1..-1].include?(':')
|
|
80
|
+
j = i + line[i+1..-1].index(':')
|
|
81
|
+
colored_string += line[i..j].colorize(:red)
|
|
82
|
+
i += j - i + 1
|
|
83
|
+
elsif line[i].match(%r{\d}) && line[i..-2].chars.map { |c| c.match(%r{\d}) }.all?
|
|
84
|
+
colored_string += line[i..-2].colorize(:blue)
|
|
85
|
+
i = line.length - 1
|
|
86
|
+
elsif line[i..i + 2] == 'nil'
|
|
87
|
+
colored_string += line[i..i + 2].colorize(:yellow)
|
|
88
|
+
i += 3
|
|
89
|
+
elsif line[i..i + 3] == 'true'
|
|
90
|
+
colored_string += line[i..i + 3].colorize(:yellow)
|
|
91
|
+
i += 4
|
|
92
|
+
elsif line[i..i + 4] == 'false'
|
|
93
|
+
colored_string += line[i..i + 4].colorize(:yellow)
|
|
94
|
+
i += 5
|
|
95
|
+
elsif line[i].match(%r{[\"\w]})
|
|
96
|
+
colored_string += line[i..-2].colorize(:green)
|
|
97
|
+
i = line.length - 1
|
|
98
|
+
elsif line[i] == ':'
|
|
99
|
+
colored_string += line[i].colorize(:yellow)
|
|
100
|
+
i += 1
|
|
101
|
+
else
|
|
102
|
+
colored_string += line[i]
|
|
103
|
+
i += 1
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
return colored_string
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
sio = StringIO.new
|
|
112
|
+
PP.pp(obj, sio)
|
|
113
|
+
|
|
114
|
+
colored_string = ''
|
|
115
|
+
i = 0
|
|
116
|
+
while i < sio.string.length
|
|
117
|
+
if ['{', '}', '[', ']', ','].include?(sio.string[i])
|
|
118
|
+
colored_string += sio.string[i]
|
|
119
|
+
i += 1
|
|
120
|
+
elsif sio.string[i] == '"'
|
|
121
|
+
capture = '"'
|
|
122
|
+
cnt = 1
|
|
123
|
+
while sio.string[i + cnt] != '"'
|
|
124
|
+
capture += sio.string[i + cnt]
|
|
125
|
+
cnt += 1
|
|
126
|
+
end
|
|
127
|
+
capture += '"'
|
|
128
|
+
color = sio.string[i + cnt + 2] == '=' ? :red : :green
|
|
129
|
+
colored_string += capture.colorize(color)
|
|
130
|
+
i += cnt + 1
|
|
131
|
+
elsif sio.string[i..i + 1] == '=>'
|
|
132
|
+
colored_string += sio.string[i..i + 1].colorize(:yellow)
|
|
133
|
+
i += 2
|
|
134
|
+
elsif sio.string[i].to_i.to_s == sio.string[i]
|
|
135
|
+
colored_string += sio.string[i].colorize(:light_blue)
|
|
136
|
+
i += 1
|
|
137
|
+
elsif sio.string[i..i + 2] == 'nil'
|
|
138
|
+
colored_string += sio.string[i..i + 2].colorize(:yellow)
|
|
139
|
+
i += 3
|
|
140
|
+
elsif sio.string[i..i + 3] == 'true'
|
|
141
|
+
colored_string += sio.string[i..i + 3].colorize(:yellow)
|
|
142
|
+
i += 4
|
|
143
|
+
elsif sio.string[i..i + 4] == 'false'
|
|
144
|
+
colored_string += sio.string[i..i + 4].colorize(:yellow)
|
|
145
|
+
i += 5
|
|
146
|
+
elsif ['new:', 'old:'].include?(sio.string[i..i + 3])
|
|
147
|
+
colored_string += sio.string[i..i + 2].colorize(:magenta)
|
|
148
|
+
colored_string += sio.string[i + 3].colorize(:yellow)
|
|
149
|
+
i += 4
|
|
150
|
+
else
|
|
151
|
+
colored_string += sio.string[i]
|
|
152
|
+
i += 1
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
return colored_string
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module Silkedit::Savegame
|
|
4
|
+
module Packer
|
|
5
|
+
RAWHEADER = "\u0000\u0001\u0000\u0000\u0000\xFF\xFF\xFF\xFF\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0001\u0000\u0000\u0000"
|
|
6
|
+
C_SHARP_HEADER = [0, 1, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0, 0, 0]
|
|
7
|
+
|
|
8
|
+
def self.can_unpack?(string)
|
|
9
|
+
return string.start_with?(RAWHEADER)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.pack(string)
|
|
13
|
+
string = Crypto.aes_encrypt(string)
|
|
14
|
+
string = Base64.strict_encode64(string)
|
|
15
|
+
string = self.add_header(string)
|
|
16
|
+
string
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.unpack(bytes)
|
|
20
|
+
bytes = self.strip_header(bytes)
|
|
21
|
+
bytes = Base64.decode64(bytes)
|
|
22
|
+
bytes = Crypto.aes_decrypt(bytes)
|
|
23
|
+
string = bytes.force_encoding('UTF-8')
|
|
24
|
+
string
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.generate_length_prefixed_string(length)
|
|
28
|
+
length = [length, 0x7FFFFFFF].min
|
|
29
|
+
bytes = []
|
|
30
|
+
while true
|
|
31
|
+
if length >> 7 != 0
|
|
32
|
+
bytes << ((length & 0x7F) | 0x80)
|
|
33
|
+
length >>= 7
|
|
34
|
+
else
|
|
35
|
+
bytes << (length & 0x7F)
|
|
36
|
+
length >>= 7
|
|
37
|
+
break
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
bytes << length if length != 0
|
|
41
|
+
bytes.pack("C*")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.add_header(data)
|
|
45
|
+
# Ensure data is a binary string
|
|
46
|
+
data = data.b if data.respond_to?(:b)
|
|
47
|
+
length_data = generate_length_prefixed_string(data.bytesize)
|
|
48
|
+
result = String.new
|
|
49
|
+
result << RAWHEADER.dup.force_encoding("ASCII-8BIT")
|
|
50
|
+
result << length_data
|
|
51
|
+
result << data
|
|
52
|
+
result << [11].pack("C")
|
|
53
|
+
result
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.strip_header(data)
|
|
57
|
+
# Remove fixed header and ending byte (11)
|
|
58
|
+
start = C_SHARP_HEADER.length
|
|
59
|
+
finish = data.bytesize - 1
|
|
60
|
+
data = data.byteslice(start...finish)
|
|
61
|
+
|
|
62
|
+
# Remove variable-length LengthPrefixedString header
|
|
63
|
+
bytes = data.bytes
|
|
64
|
+
length_count = 0
|
|
65
|
+
bytes.each do |byte|
|
|
66
|
+
length_count += 1
|
|
67
|
+
break if (byte & 0x80) == 0
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Slice off the length prefix header
|
|
71
|
+
stripped = bytes[length_count..-1] || []
|
|
72
|
+
stripped.pack("C*")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module Silkedit::Savegame
|
|
2
|
+
class SaveFile
|
|
3
|
+
def initialize(game, savegame_index)
|
|
4
|
+
# Rbcli::Warehouse.get(:config, :parsedopts)
|
|
5
|
+
Rbcli.log.fatal('Running on unknown OS. Exiting.', exit_status: 25) if Silkedit::Sys.os.nil?
|
|
6
|
+
|
|
7
|
+
config = YAML.safe_load_file(File.join(Silkedit::LIBDIR, 'config', 'savegame.yaml'), symbolize_names: true)
|
|
8
|
+
|
|
9
|
+
@max_backups = config[:max_backups]
|
|
10
|
+
@use_yaml = config[:use_yaml]
|
|
11
|
+
@game = game
|
|
12
|
+
@data = nil
|
|
13
|
+
|
|
14
|
+
basename = "user#{savegame_index}"
|
|
15
|
+
@dirs = {
|
|
16
|
+
saves: File.expand_path(config[:savegame_directory][@game][Silkedit::Sys.os]),
|
|
17
|
+
work: File.join(Silkedit::Sys.tmpdir, @game.to_s),
|
|
18
|
+
backups: File.join(Silkedit::Sys.tmpdir, @game.to_s)
|
|
19
|
+
}
|
|
20
|
+
if @dirs[:saves].include?('%STEAMID%')
|
|
21
|
+
@dirs[:saves] = Dir.glob(File.join(File.dirname(@dirs[:saves]), '*')).select { |f| File.basename(f).match(%r{\d+}) }.first
|
|
22
|
+
end
|
|
23
|
+
@dirs.each_value { |dir| FileUtils.mkdir_p(dir) unless File.directory?(dir) }
|
|
24
|
+
@filenames = {
|
|
25
|
+
dat: File.join(@dirs[:saves], "#{basename}.dat"),
|
|
26
|
+
json: File.join(@dirs[:work], "#{basename}.#{@use_yaml ? 'yaml' : 'json'}"),
|
|
27
|
+
backup: File.join(@dirs[:backups], "#{basename}.bak")
|
|
28
|
+
}
|
|
29
|
+
@filenames[:backups] = self.get_backup_file_list
|
|
30
|
+
Rbcli.log.debug "Savegame directory: #{@dirs[:saves]}", 'SAVEGAME'
|
|
31
|
+
Rbcli.log.debug "Working directory: #{@dirs[:work]}", 'SAVEGAME'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_accessor :data
|
|
35
|
+
attr_reader :dirs, :filenames, :game
|
|
36
|
+
|
|
37
|
+
def loaded?
|
|
38
|
+
!@data.nil?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def load_from_dat
|
|
42
|
+
load_from_disk(@filenames[:dat])
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def save_to_dat
|
|
47
|
+
save_to_disk(@filenames[:dat], pack: true)
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def load_from_json
|
|
52
|
+
load_from_disk(@filenames[:json])
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def save_to_json
|
|
57
|
+
save_to_disk(@filenames[:json], pack: false)
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def del_json
|
|
62
|
+
FileUtils.rm(@filenames[:json])
|
|
63
|
+
self
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def load_from_backup(seq_number: nil)
|
|
67
|
+
all_backup_files = self.get_backup_file_list
|
|
68
|
+
return false if all_backup_files.empty?
|
|
69
|
+
selected_file = seq_number.nil? ? all_backup_files.last : "#{@filenames[:backup]}#{seq_number}"
|
|
70
|
+
return false unless File.exist?(selected_file)
|
|
71
|
+
load_from_disk(selected_file)
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def save_to_backup(backup_name: nil)
|
|
76
|
+
if backup_name.nil?
|
|
77
|
+
return false if @max_backups <= 0
|
|
78
|
+
self.remove_old_backups
|
|
79
|
+
backup_filename = "#{@filenames[:backup]}#{self.next_backup_seq_number}"
|
|
80
|
+
else
|
|
81
|
+
backup_filename = "#{@filenames[:backup]}.#{backup_name}"
|
|
82
|
+
end
|
|
83
|
+
save_to_disk(backup_filename, pack: true)
|
|
84
|
+
backup_filename
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def direct_backup(backup_name: nil)
|
|
88
|
+
if backup_name.nil?
|
|
89
|
+
return false if @max_backups <= 0
|
|
90
|
+
self.remove_old_backups
|
|
91
|
+
backup_filename = "#{@filenames[:backup]}#{self.next_backup_seq_number}"
|
|
92
|
+
else
|
|
93
|
+
backup_filename = "#{@filenames[:backup]}.#{backup_name}"
|
|
94
|
+
end
|
|
95
|
+
FileUtils.cp(@filenames[:dat], backup_filename)
|
|
96
|
+
@filenames[:backups] = self.get_backup_file_list
|
|
97
|
+
Rbcli.log.info "Backed up savefile to #{backup_filename}", 'SAVEGAME'
|
|
98
|
+
backup_filename
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def direct_restore(seq_number: nil, backup_name: nil)
|
|
102
|
+
if seq_number.nil? && backup_name.nil?
|
|
103
|
+
all_backup_files = self.get_backup_file_list
|
|
104
|
+
return false if all_backup_files.empty?
|
|
105
|
+
selected_file = all_backup_files.last
|
|
106
|
+
elsif !seq_number.nil?
|
|
107
|
+
all_seq_numbers = self.get_backup_file_list.map { |f| File.extname(f).scan(/\d+$/).first }.reject(&:nil?).map { |n| n.to_i }
|
|
108
|
+
return false unless all_seq_numbers.include?(seq_number)
|
|
109
|
+
selected_file = "#{@filenames[:backup]}#{seq_number}"
|
|
110
|
+
elsif !backup_name.nil?
|
|
111
|
+
return false unless File.exist?("#{@filenames[:backup]}.#{backup_name}")
|
|
112
|
+
selected_file = "#{@filenames[:backup]}.#{backup_name}"
|
|
113
|
+
end
|
|
114
|
+
FileUtils.cp(selected_file, @filenames[:dat])
|
|
115
|
+
Rbcli.log.info "Restored savefile from #{selected_file}", 'SAVEGAME'
|
|
116
|
+
selected_file
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def load_from_disk(filename)
|
|
122
|
+
begin
|
|
123
|
+
data = File.read(filename)
|
|
124
|
+
rescue Errno::ENOENT
|
|
125
|
+
Rbcli.log.fatal("File not found: #{filename}.", exit_status: 25)
|
|
126
|
+
end
|
|
127
|
+
data = Packer.unpack(data) if Packer.can_unpack?(data)
|
|
128
|
+
Rbcli.log.info "Loaded from #{File.basename(filename)}", 'SAVEGAME'
|
|
129
|
+
@data = YAML.safe_load(data)
|
|
130
|
+
# @data = JSON.parse(data)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def save_to_disk(filename, pack: false)
|
|
134
|
+
data = (@use_yaml && !pack) ? YAML.safe_dump(@data) : JSON.pretty_generate(@data)
|
|
135
|
+
data = Packer.pack(data) if pack
|
|
136
|
+
File.write(filename, data)
|
|
137
|
+
Rbcli.log.info "Saved to #{File.basename(filename)}", 'SAVEGAME'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def get_backup_file_list
|
|
141
|
+
Dir.glob("#{@filenames[:backup]}*").select { |f| f.match?(/#{File.basename(@filenames[:backup])}\d+$/) }.sort_by { |f| File.extname(f).scan(/\d+$/).first.to_i }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def remove_old_backups
|
|
145
|
+
all_backup_files = self.get_backup_file_list
|
|
146
|
+
while !all_backup_files.empty? && all_backup_files.length >= @max_backups
|
|
147
|
+
FileUtils.rm(all_backup_files.shift)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def next_backup_seq_number
|
|
152
|
+
all_backup_files = self.get_backup_file_list
|
|
153
|
+
return 0 if all_backup_files.empty?
|
|
154
|
+
all_backup_files.map { |f| File.extname(f).scan(/\d+$/).first }.reject(&:nil?).map { |n| n.to_i }.max + 1
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'io/console'
|
|
2
|
+
|
|
3
|
+
module Silkedit::Sys
|
|
4
|
+
def self.os
|
|
5
|
+
case RbConfig::CONFIG['host_os'].to_s
|
|
6
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|windows/i
|
|
7
|
+
:windows
|
|
8
|
+
when /darwin|mac os|macos|osx/i
|
|
9
|
+
:macos
|
|
10
|
+
when /linux/i
|
|
11
|
+
:linux
|
|
12
|
+
else
|
|
13
|
+
nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.tmpdir
|
|
18
|
+
case Silkedit::Sys.os
|
|
19
|
+
when :windows
|
|
20
|
+
File.join(ENV['TEMP'] || ENV['TMP'], 'silkedit')
|
|
21
|
+
when :macos
|
|
22
|
+
File.join(ENV['TMPDIR'], 'silkedit')
|
|
23
|
+
when :linux
|
|
24
|
+
File.join(ENV['TMPDIR'], 'silkedit')
|
|
25
|
+
else
|
|
26
|
+
Dir.tmpdir
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.yes_no? question, &block
|
|
31
|
+
print "#{question} (y/n): "
|
|
32
|
+
answer = STDIN.getch.chomp.downcase until %w[y n].include?(answer)
|
|
33
|
+
print "#{answer}\n"
|
|
34
|
+
yield if block_given? && answer == 'y'
|
|
35
|
+
return answer == 'y'
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/silkedit.rb
ADDED
data/sig/silkedit.rbs
ADDED