silkedit 0.1.0

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.
Files changed (282) hide show
  1. checksums.yaml +7 -0
  2. data/.rbcli +0 -0
  3. data/CHANGELOG.md +3 -0
  4. data/CODE_OF_CONDUCT.md +132 -0
  5. data/README.md +302 -0
  6. data/Rakefile +8 -0
  7. data/exe/silkedit +14 -0
  8. data/lib/commands/backup_restore.rb +25 -0
  9. data/lib/commands/cheat.rb +19 -0
  10. data/lib/commands/edit_diff.rb +47 -0
  11. data/lib/commands/examples/command.rb +51 -0
  12. data/lib/commands/examples/script.rb +36 -0
  13. data/lib/commands/journal.rb +34 -0
  14. data/lib/commands/permasave_permaload.rb +62 -0
  15. data/lib/commands/scripts/.keep +0 -0
  16. data/lib/commands/unpack_repack.rb +27 -0
  17. data/lib/commands/zone_mkzone.rb +102 -0
  18. data/lib/config/config.rb +28 -0
  19. data/lib/config/envvars.rb +23 -0
  20. data/lib/config/hooks.rb +22 -0
  21. data/lib/config/logger.rb +36 -0
  22. data/lib/config/parser.rb +34 -0
  23. data/lib/config/updatechecker.rb +12 -0
  24. data/lib/silkedit/cheats/cheatengine.rb +34 -0
  25. data/lib/silkedit/cheats/hollow_knight_cheats.rb +5 -0
  26. data/lib/silkedit/cheats/merger.rb +103 -0
  27. data/lib/silkedit/cheats/silksong_cheats.rb +121 -0
  28. data/lib/silkedit/cheats/silksong_enemyjournal.rb +119 -0
  29. data/lib/silkedit/cheats/silksong_zoner.rb +98 -0
  30. data/lib/silkedit/config/savegame.yaml +13 -0
  31. data/lib/silkedit/config/silkedit.yaml +6 -0
  32. data/lib/silkedit/config/silksong/cheatdata.yaml +1145 -0
  33. data/lib/silkedit/config/silksong/enemylist.yaml +1418 -0
  34. data/lib/silkedit/config/silksong/old.yaml +4345 -0
  35. data/lib/silkedit/config/silksong/permasaves.yaml +10 -0
  36. data/lib/silkedit/config/silksong/unfinished.yaml +628 -0
  37. data/lib/silkedit/config/silksong/zones.yaml +1686 -0
  38. data/lib/silkedit/images/.keep +0 -0
  39. data/lib/silkedit/images/silksong/Aknid.png +0 -0
  40. data/lib/silkedit/images/silksong/Alita.png +0 -0
  41. data/lib/silkedit/images/silksong/Barnak.png +0 -0
  42. data/lib/silkedit/images/silksong/Beastfly.png +0 -0
  43. data/lib/silkedit/images/silksong/Bell_Beast.png +0 -0
  44. data/lib/silkedit/images/silksong/Bell_Eater.png +0 -0
  45. data/lib/silkedit/images/silksong/Bloatroach.png +0 -0
  46. data/lib/silkedit/images/silksong/Broodmother.png +0 -0
  47. data/lib/silkedit/images/silksong/Brushflit.png +0 -0
  48. data/lib/silkedit/images/silksong/Burning_Bug.png +0 -0
  49. data/lib/silkedit/images/silksong/Caranid.png +0 -0
  50. data/lib/silkedit/images/silksong/Choir_Bellbearer.png +0 -0
  51. data/lib/silkedit/images/silksong/Choir_Clapper.png +0 -0
  52. data/lib/silkedit/images/silksong/Choir_Elder.png +0 -0
  53. data/lib/silkedit/images/silksong/Choir_Flyer.png +0 -0
  54. data/lib/silkedit/images/silksong/Choir_Hornhead.png +0 -0
  55. data/lib/silkedit/images/silksong/Choir_Pouncer.png +0 -0
  56. data/lib/silkedit/images/silksong/Choristor.png +0 -0
  57. data/lib/silkedit/images/silksong/Clawmaiden.png +0 -0
  58. data/lib/silkedit/images/silksong/Clover_Dancers.png +0 -0
  59. data/lib/silkedit/images/silksong/Cloverstag.png +0 -0
  60. data/lib/silkedit/images/silksong/Cogwork_Choirbug.png +0 -0
  61. data/lib/silkedit/images/silksong/Cogwork_Clapper.png +0 -0
  62. data/lib/silkedit/images/silksong/Cogwork_Cleanser.png +0 -0
  63. data/lib/silkedit/images/silksong/Cogwork_Crawler.png +0 -0
  64. data/lib/silkedit/images/silksong/Cogwork_Dancers.png +0 -0
  65. data/lib/silkedit/images/silksong/Cogwork_Defender.png +0 -0
  66. data/lib/silkedit/images/silksong/Cogwork_Hauler.png +0 -0
  67. data/lib/silkedit/images/silksong/Cogwork_Spine.png +0 -0
  68. data/lib/silkedit/images/silksong/Cogwork_Underfly.png +0 -0
  69. data/lib/silkedit/images/silksong/Cogworker.png +0 -0
  70. data/lib/silkedit/images/silksong/Conchfly.png +0 -0
  71. data/lib/silkedit/images/silksong/Coral_Furm.png +0 -0
  72. data/lib/silkedit/images/silksong/Corrcrust_Karaka.png +0 -0
  73. data/lib/silkedit/images/silksong/Covetous_Pilgrim.png +0 -0
  74. data/lib/silkedit/images/silksong/Craggler.png +0 -0
  75. data/lib/silkedit/images/silksong/Cragglite.png +0 -0
  76. data/lib/silkedit/images/silksong/Craw.png +0 -0
  77. data/lib/silkedit/images/silksong/Craw_Juror.png +0 -0
  78. data/lib/silkedit/images/silksong/Crawfather.png +0 -0
  79. data/lib/silkedit/images/silksong/Crust_King_Khann.png +0 -0
  80. data/lib/silkedit/images/silksong/Crustcrag.png +0 -0
  81. data/lib/silkedit/images/silksong/Crustcrawler.png +0 -0
  82. data/lib/silkedit/images/silksong/Deep_Diver.png +0 -0
  83. data/lib/silkedit/images/silksong/Disgraced_Chef_Lugoli.png +0 -0
  84. data/lib/silkedit/images/silksong/Drapefly.png +0 -0
  85. data/lib/silkedit/images/silksong/Drapelord.png +0 -0
  86. data/lib/silkedit/images/silksong/Drapemite.png +0 -0
  87. data/lib/silkedit/images/silksong/Dreg_Catcher.png +0 -0
  88. data/lib/silkedit/images/silksong/Dreg_Husk.png +0 -0
  89. data/lib/silkedit/images/silksong/Dregwheel.png +0 -0
  90. data/lib/silkedit/images/silksong/Driftlin.png +0 -0
  91. data/lib/silkedit/images/silksong/Driznarga.png +0 -0
  92. data/lib/silkedit/images/silksong/Driznit.png +0 -0
  93. data/lib/silkedit/images/silksong/Ductsucker.png +0 -0
  94. data/lib/silkedit/images/silksong/Elder_Pilgrim.png +0 -0
  95. data/lib/silkedit/images/silksong/Envoy.png +0 -0
  96. data/lib/silkedit/images/silksong/Escalion.png +0 -0
  97. data/lib/silkedit/images/silksong/Father_of_the_Flame.png +0 -0
  98. data/lib/silkedit/images/silksong/Fertid.png +0 -0
  99. data/lib/silkedit/images/silksong/First_Sinner.png +0 -0
  100. data/lib/silkedit/images/silksong/Flapping_Fertid.png +0 -0
  101. data/lib/silkedit/images/silksong/Flintbeetle.png +0 -0
  102. data/lib/silkedit/images/silksong/Flintflame_Flyer.png +0 -0
  103. data/lib/silkedit/images/silksong/Flintstone_Flyer.png +0 -0
  104. data/lib/silkedit/images/silksong/Fluttermite.png +0 -0
  105. data/lib/silkedit/images/silksong/Forebrothers_Signis_&_Gron.png +0 -0
  106. data/lib/silkedit/images/silksong/Fourth_Chorus.png +0 -0
  107. data/lib/silkedit/images/silksong/Freshfly.png +0 -0
  108. data/lib/silkedit/images/silksong/Furm.png +0 -0
  109. data/lib/silkedit/images/silksong/Gahlia.png +0 -0
  110. data/lib/silkedit/images/silksong/Gargant_Gloom.png +0 -0
  111. data/lib/silkedit/images/silksong/Garpid.png +0 -0
  112. data/lib/silkedit/images/silksong/Giant_Drapemite.png +0 -0
  113. data/lib/silkedit/images/silksong/Gloomsac.png +0 -0
  114. data/lib/silkedit/images/silksong/Grand_Mother_Silk.png +0 -0
  115. data/lib/silkedit/images/silksong/Grand_Reed.png +0 -0
  116. data/lib/silkedit/images/silksong/Great_Conchfly.png +0 -0
  117. data/lib/silkedit/images/silksong/Groal_the_Great.png +0 -0
  118. data/lib/silkedit/images/silksong/Grom.png +0 -0
  119. data/lib/silkedit/images/silksong/Gromling.png +0 -0
  120. data/lib/silkedit/images/silksong/Guardfly.png +0 -0
  121. data/lib/silkedit/images/silksong/Gurr_the_Outcast.png +0 -0
  122. data/lib/silkedit/images/silksong/Hardbone_Elder.png +0 -0
  123. data/lib/silkedit/images/silksong/Hardbone_Hopper.png +0 -0
  124. data/lib/silkedit/images/silksong/Hoker.png +0 -0
  125. data/lib/silkedit/images/silksong/Huge_Flea.png +0 -0
  126. data/lib/silkedit/images/silksong/Imoba.png +0 -0
  127. data/lib/silkedit/images/silksong/Judge.png +0 -0
  128. data/lib/silkedit/images/silksong/Kai.png +0 -0
  129. data/lib/silkedit/images/silksong/Kakri.png +0 -0
  130. data/lib/silkedit/images/silksong/Karak_Gor.png +0 -0
  131. data/lib/silkedit/images/silksong/Karaka.png +0 -0
  132. data/lib/silkedit/images/silksong/Kilik.png +0 -0
  133. data/lib/silkedit/images/silksong/Kindanir.png +0 -0
  134. data/lib/silkedit/images/silksong/Lace.png +0 -0
  135. data/lib/silkedit/images/silksong/Lampbearer.png +0 -0
  136. data/lib/silkedit/images/silksong/Last_Claw.png +0 -0
  137. data/lib/silkedit/images/silksong/Last_Judge.png +0 -0
  138. data/lib/silkedit/images/silksong/Lavalarga.png +0 -0
  139. data/lib/silkedit/images/silksong/Lavalug.png +0 -0
  140. data/lib/silkedit/images/silksong/Leaf_Glider.png +0 -0
  141. data/lib/silkedit/images/silksong/Leaf_Roller.png +0 -0
  142. data/lib/silkedit/images/silksong/Lost_Garmond.png +0 -0
  143. data/lib/silkedit/images/silksong/Lost_Lace.png +0 -0
  144. data/lib/silkedit/images/silksong/Maestro.png +0 -0
  145. data/lib/silkedit/images/silksong/Marrowmaw.png +0 -0
  146. data/lib/silkedit/images/silksong/Massive_Mossgrub.png +0 -0
  147. data/lib/silkedit/images/silksong/Mawling.png +0 -0
  148. data/lib/silkedit/images/silksong/Memoria.png +0 -0
  149. data/lib/silkedit/images/silksong/Minister.png +0 -0
  150. data/lib/silkedit/images/silksong/Miremite.png +0 -0
  151. data/lib/silkedit/images/silksong/Mite.png +0 -0
  152. data/lib/silkedit/images/silksong/Mitemother.png +0 -0
  153. data/lib/silkedit/images/silksong/Mnemonid.png +0 -0
  154. data/lib/silkedit/images/silksong/Mnemonord.png +0 -0
  155. data/lib/silkedit/images/silksong/Moorwing.png +0 -0
  156. data/lib/silkedit/images/silksong/Mortician.png +0 -0
  157. data/lib/silkedit/images/silksong/Moss_Mother.png +0 -0
  158. data/lib/silkedit/images/silksong/Mossgrub.png +0 -0
  159. data/lib/silkedit/images/silksong/Mossmir.png +0 -0
  160. data/lib/silkedit/images/silksong/Mothleaf_Lagnia.png +0 -0
  161. data/lib/silkedit/images/silksong/Muckmaggot.png +0 -0
  162. data/lib/silkedit/images/silksong/Muckroach.png +0 -0
  163. data/lib/silkedit/images/silksong/Nuphar.png +0 -0
  164. data/lib/silkedit/images/silksong/Nyleth.png +0 -0
  165. data/lib/silkedit/images/silksong/Overgrown_Pilgrim.png +0 -0
  166. data/lib/silkedit/images/silksong/Palestag.png +0 -0
  167. data/lib/silkedit/images/silksong/Pendra.png +0 -0
  168. data/lib/silkedit/images/silksong/Pendragor.png +0 -0
  169. data/lib/silkedit/images/silksong/Penitent.png +0 -0
  170. data/lib/silkedit/images/silksong/Phacia.png +0 -0
  171. data/lib/silkedit/images/silksong/Phantom.png +0 -0
  172. data/lib/silkedit/images/silksong/Pharlid.png +0 -0
  173. data/lib/silkedit/images/silksong/Pharlid_Diver.png +0 -0
  174. data/lib/silkedit/images/silksong/Pilgrim_Bellbearer.png +0 -0
  175. data/lib/silkedit/images/silksong/Pilgrim_Groveller.png +0 -0
  176. data/lib/silkedit/images/silksong/Pilgrim_Guide.png +0 -0
  177. data/lib/silkedit/images/silksong/Pilgrim_Hiker.png +0 -0
  178. data/lib/silkedit/images/silksong/Pilgrim_Hornfly.png +0 -0
  179. data/lib/silkedit/images/silksong/Pilgrim_Hulk.png +0 -0
  180. data/lib/silkedit/images/silksong/Pilgrim_Pouncer.png +0 -0
  181. data/lib/silkedit/images/silksong/Pinstress.png +0 -0
  182. data/lib/silkedit/images/silksong/Plasmid.png +0 -0
  183. data/lib/silkedit/images/silksong/Plasmidas.png +0 -0
  184. data/lib/silkedit/images/silksong/Plasmified_Zango.png +0 -0
  185. data/lib/silkedit/images/silksong/Pokenabbin.png +0 -0
  186. data/lib/silkedit/images/silksong/Pollenica.png +0 -0
  187. data/lib/silkedit/images/silksong/Pond_Skipper.png +0 -0
  188. data/lib/silkedit/images/silksong/Pondcatcher.png +0 -0
  189. data/lib/silkedit/images/silksong/Puny_Penitent.png +0 -0
  190. data/lib/silkedit/images/silksong/Reed.png +0 -0
  191. data/lib/silkedit/images/silksong/Rhinogrund.png +0 -0
  192. data/lib/silkedit/images/silksong/Roachcatcher.png +0 -0
  193. data/lib/silkedit/images/silksong/Roachfeeder.png +0 -0
  194. data/lib/silkedit/images/silksong/Roachkeeper.png +0 -0
  195. data/lib/silkedit/images/silksong/Roachserver.png +0 -0
  196. data/lib/silkedit/images/silksong/Sandcarver.png +0 -0
  197. data/lib/silkedit/images/silksong/Savage_Beastfly.png +0 -0
  198. data/lib/silkedit/images/silksong/Scabfly.png +0 -0
  199. data/lib/silkedit/images/silksong/Scrollreader.png +0 -0
  200. data/lib/silkedit/images/silksong/Second_Sentinel.png +0 -0
  201. data/lib/silkedit/images/silksong/Servitor_Boran.png +0 -0
  202. data/lib/silkedit/images/silksong/Servitor_Ignim.png +0 -0
  203. data/lib/silkedit/images/silksong/Shadow_Charger.png +0 -0
  204. data/lib/silkedit/images/silksong/Shadow_Creeper_(Silksong).png +0 -0
  205. data/lib/silkedit/images/silksong/Shardillard.png +0 -0
  206. data/lib/silkedit/images/silksong/Shellwood_Gnat.png +0 -0
  207. data/lib/silkedit/images/silksong/Shrine_Guardian_Seth.png +0 -0
  208. data/lib/silkedit/images/silksong/Silk_Snipper.png +0 -0
  209. data/lib/silkedit/images/silksong/Sister_Splinter.png +0 -0
  210. data/lib/silkedit/images/silksong/Skarr_Scout.png +0 -0
  211. data/lib/silkedit/images/silksong/Skarr_Stalker.png +0 -0
  212. data/lib/silkedit/images/silksong/Skarrgard.png +0 -0
  213. data/lib/silkedit/images/silksong/Skarrlid.png +0 -0
  214. data/lib/silkedit/images/silksong/Skarrsinger_Karmelita.png +0 -0
  215. data/lib/silkedit/images/silksong/Skarrwing.png +0 -0
  216. data/lib/silkedit/images/silksong/Skrill.png +0 -0
  217. data/lib/silkedit/images/silksong/Skull_Brute.png +0 -0
  218. data/lib/silkedit/images/silksong/Skull_Scuttler.png +0 -0
  219. data/lib/silkedit/images/silksong/Skull_Tyrant.png +0 -0
  220. data/lib/silkedit/images/silksong/Skullwing.png +0 -0
  221. data/lib/silkedit/images/silksong/Slubberlug.png +0 -0
  222. data/lib/silkedit/images/silksong/Smelt_Shoveller.png +0 -0
  223. data/lib/silkedit/images/silksong/Smokerock_Sifter.png +0 -0
  224. data/lib/silkedit/images/silksong/Snitchfly.png +0 -0
  225. data/lib/silkedit/images/silksong/Spear_Skarr.png +0 -0
  226. data/lib/silkedit/images/silksong/Spinebeak_Kai.png +0 -0
  227. data/lib/silkedit/images/silksong/Spit_Squit.png +0 -0
  228. data/lib/silkedit/images/silksong/Splinter.png +0 -0
  229. data/lib/silkedit/images/silksong/Splinterbark.png +0 -0
  230. data/lib/silkedit/images/silksong/Splinterhorn.png +0 -0
  231. data/lib/silkedit/images/silksong/Squatcraw.png +0 -0
  232. data/lib/silkedit/images/silksong/Squatcraw_Juror.png +0 -0
  233. data/lib/silkedit/images/silksong/Squirrm.png +0 -0
  234. data/lib/silkedit/images/silksong/Steelspine_Kai.png +0 -0
  235. data/lib/silkedit/images/silksong/Stilkin.png +0 -0
  236. data/lib/silkedit/images/silksong/Stilkin_Trapper.png +0 -0
  237. data/lib/silkedit/images/silksong/Summoned_Saviour.png +0 -0
  238. data/lib/silkedit/images/silksong/Surgeon.png +0 -0
  239. data/lib/silkedit/images/silksong/Swamp_Squit.png +0 -0
  240. data/lib/silkedit/images/silksong/Tallcraw.png +0 -0
  241. data/lib/silkedit/images/silksong/Tallcraw_Juror.png +0 -0
  242. data/lib/silkedit/images/silksong/Tarmite.png +0 -0
  243. data/lib/silkedit/images/silksong/The_Unravelled.png +0 -0
  244. data/lib/silkedit/images/silksong/Thread_Raker.png +0 -0
  245. data/lib/silkedit/images/silksong/Tormented_Trobbio.png +0 -0
  246. data/lib/silkedit/images/silksong/Trobbio.png +0 -0
  247. data/lib/silkedit/images/silksong/Undercrank.png +0 -0
  248. data/lib/silkedit/images/silksong/Underloft.png +0 -0
  249. data/lib/silkedit/images/silksong/Underpoke.png +0 -0
  250. data/lib/silkedit/images/silksong/Underscrub.png +0 -0
  251. data/lib/silkedit/images/silksong/Undersweep.png +0 -0
  252. data/lib/silkedit/images/silksong/Underworker.png +0 -0
  253. data/lib/silkedit/images/silksong/Vaultborn.png +0 -0
  254. data/lib/silkedit/images/silksong/Vaultkeeper.png +0 -0
  255. data/lib/silkedit/images/silksong/Verdanir.png +0 -0
  256. data/lib/silkedit/images/silksong/Vicious_Caranid.png +0 -0
  257. data/lib/silkedit/images/silksong/Void_Mass.png +0 -0
  258. data/lib/silkedit/images/silksong/Void_Tendrils_(Silksong).png +0 -0
  259. data/lib/silkedit/images/silksong/Voltvyrm.png +0 -0
  260. data/lib/silkedit/images/silksong/Wardenfly.png +0 -0
  261. data/lib/silkedit/images/silksong/Watcher_at_the_Edge.png +0 -0
  262. data/lib/silkedit/images/silksong/Widow.png +0 -0
  263. data/lib/silkedit/images/silksong/Winged_Furm.png +0 -0
  264. data/lib/silkedit/images/silksong/Winged_Lifeseed.png +0 -0
  265. data/lib/silkedit/images/silksong/Winged_Pilgrim.png +0 -0
  266. data/lib/silkedit/images/silksong/Winged_Pilgrim_Bellbearer.png +0 -0
  267. data/lib/silkedit/images/silksong/Wingmould_(Silksong).png +0 -0
  268. data/lib/silkedit/images/silksong/Wisp.png +0 -0
  269. data/lib/silkedit/images/silksong/Wood_Wasp.png +0 -0
  270. data/lib/silkedit/images/silksong/Wraith.png +0 -0
  271. data/lib/silkedit/images/silksong/Yago.png +0 -0
  272. data/lib/silkedit/images/silksong/Yuma.png +0 -0
  273. data/lib/silkedit/images/silksong/Yumama.png +0 -0
  274. data/lib/silkedit/savegame/crypto.rb +25 -0
  275. data/lib/silkedit/savegame/diff.rb +158 -0
  276. data/lib/silkedit/savegame/packer.rb +75 -0
  277. data/lib/silkedit/savegame/savefile.rb +153 -0
  278. data/lib/silkedit/util/system.rb +37 -0
  279. data/lib/silkedit/version.rb +5 -0
  280. data/lib/silkedit.rb +9 -0
  281. data/sig/silkedit.rbs +4 -0
  282. metadata +504 -0
@@ -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,158 @@
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)
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)
17
+ diff = []
18
+ if old.first.is_a?(Hash)
19
+ if old.first.key?('Name')
20
+ keycomps = %w[Name]
21
+ elsif old.first.key?('SceneName') && old.first.key?('ID')
22
+ keycomps = %w[SceneName ID]
23
+ elsif old.first.key?('SceneName') && old.first.key?('EventType')
24
+ keycomps = %w[SceneName EventType]
25
+ else
26
+ keycomps = nil
27
+ end
28
+ if keycomps.nil?
29
+ diff = { old: old.reject { |e| new.include?(e) }, new: new.reject { |e| old.include?(e) } }
30
+ else
31
+ old.each do |o|
32
+ n = new.find { |e| keycomps.map { |k| e[k] == o[k] }.reduce { |a, b| a && b } }
33
+ itemdiff = mkdiff(
34
+ o.reject { |k, _v| keycomps.include?(k) },
35
+ n.nil? ? nil : n.reject { |k, _v| keycomps.include?(k) }
36
+ )
37
+ next if itemdiff.empty?
38
+ newobj = {}
39
+ keycomps.each { |k| newobj[k] = o[k] }
40
+ diff.append(newobj.merge(itemdiff))
41
+ end
42
+ new.each do |n|
43
+ o = old.find { |e| keycomps.map { |k| e[k] == n[k] }.reduce { |a, b| a && b } }
44
+ next unless o.nil?
45
+ itemdiff = mkdiff(
46
+ nil,
47
+ n.reject { |k, _v| keycomps.include?(k) }
48
+ )
49
+ newobj = {}
50
+ keycomps.each { |k| newobj[k] = n[k] }
51
+ diff.append(newobj.merge(itemdiff))
52
+ end
53
+ end
54
+ end
55
+ elsif old != new
56
+ diff = { old: old, new: new }
57
+ end
58
+ return diff
59
+ end
60
+
61
+ def self.parse_diff(obj, yaml_output: false)
62
+ if yaml_output
63
+ sio = StringIO.new
64
+ YAML.dump(obj, sio)
65
+ colored_string = ''
66
+
67
+ sio.string.each_line do |line|
68
+ i = 0
69
+ while i < line.length
70
+ blank_leadup = line[0..i-1].chars.map { |c| c == ' ' }.all?
71
+ if line[i] == '-' && blank_leadup
72
+ colored_string += line[i].colorize(:yellow)
73
+ i += 1
74
+ elsif line[i] == ':' && blank_leadup
75
+ j = i + line[i+1..-1].index(':')
76
+ colored_string += line[i..j].colorize(:magenta)
77
+ i += j - i + 1
78
+ elsif line[i].match(%r{[\"\w]}) && line[i+1..-1].include?(':')
79
+ j = i + line[i+1..-1].index(':')
80
+ colored_string += line[i..j].colorize(:red)
81
+ i += j - i + 1
82
+ elsif line[i].match(%r{\d}) && line[i..-2].chars.map { |c| c.match(%r{\d}) }.all?
83
+ colored_string += line[i..-2].colorize(:blue)
84
+ i = line.length - 1
85
+ elsif line[i..i + 2] == 'nil'
86
+ colored_string += line[i..i + 2].colorize(:yellow)
87
+ i += 3
88
+ elsif line[i..i + 3] == 'true'
89
+ colored_string += line[i..i + 3].colorize(:yellow)
90
+ i += 4
91
+ elsif line[i..i + 4] == 'false'
92
+ colored_string += line[i..i + 4].colorize(:yellow)
93
+ i += 5
94
+ elsif line[i].match(%r{[\"\w]})
95
+ colored_string += line[i..-2].colorize(:green)
96
+ i = line.length - 1
97
+ elsif line[i] == ':'
98
+ colored_string += line[i].colorize(:yellow)
99
+ i += 1
100
+ else
101
+ colored_string += line[i]
102
+ i += 1
103
+ end
104
+ end
105
+ end
106
+
107
+ return colored_string
108
+ end
109
+
110
+ sio = StringIO.new
111
+ PP.pp(obj, sio)
112
+
113
+ colored_string = ''
114
+ i = 0
115
+ while i < sio.string.length
116
+ if ['{', '}', '[', ']', ','].include?(sio.string[i])
117
+ colored_string += sio.string[i]
118
+ i += 1
119
+ elsif sio.string[i] == '"'
120
+ capture = '"'
121
+ cnt = 1
122
+ while sio.string[i + cnt] != '"'
123
+ capture += sio.string[i + cnt]
124
+ cnt += 1
125
+ end
126
+ capture += '"'
127
+ color = sio.string[i + cnt + 2] == '=' ? :red : :green
128
+ colored_string += capture.colorize(color)
129
+ i += cnt + 1
130
+ elsif sio.string[i..i + 1] == '=>'
131
+ colored_string += sio.string[i..i + 1].colorize(:yellow)
132
+ i += 2
133
+ elsif sio.string[i].to_i.to_s == sio.string[i]
134
+ colored_string += sio.string[i].colorize(:light_blue)
135
+ i += 1
136
+ elsif sio.string[i..i + 2] == 'nil'
137
+ colored_string += sio.string[i..i + 2].colorize(:yellow)
138
+ i += 3
139
+ elsif sio.string[i..i + 3] == 'true'
140
+ colored_string += sio.string[i..i + 3].colorize(:yellow)
141
+ i += 4
142
+ elsif sio.string[i..i + 4] == 'false'
143
+ colored_string += sio.string[i..i + 4].colorize(:yellow)
144
+ i += 5
145
+ elsif ['new:', 'old:'].include?(sio.string[i..i + 3])
146
+ colored_string += sio.string[i..i + 2].colorize(:magenta)
147
+ colored_string += sio.string[i + 3].colorize(:yellow)
148
+ i += 4
149
+ else
150
+ colored_string += sio.string[i]
151
+ i += 1
152
+ end
153
+ end
154
+ return colored_string
155
+ end
156
+
157
+ end
158
+ 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,153 @@
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
+ data = File.read(filename)
123
+ data = Packer.unpack(data) if Packer.can_unpack?(data)
124
+ Rbcli.log.info "Loaded from #{File.basename(filename)}", 'SAVEGAME'
125
+ @data = YAML.safe_load(data)
126
+ # @data = JSON.parse(data)
127
+ end
128
+
129
+ def save_to_disk(filename, pack: false)
130
+ data = (@use_yaml && !pack) ? YAML.safe_dump(@data) : JSON.pretty_generate(@data)
131
+ data = Packer.pack(data) if pack
132
+ File.write(filename, data)
133
+ Rbcli.log.info "Saved to #{File.basename(filename)}", 'SAVEGAME'
134
+ end
135
+
136
+ def get_backup_file_list
137
+ Dir.glob("#{@filenames[:backup]}*").select { |f| f.match?(/#{File.basename(@filenames[:backup])}\d+$/) }.sort_by { |f| File.extname(f).scan(/\d+$/).first.to_i }
138
+ end
139
+
140
+ def remove_old_backups
141
+ all_backup_files = self.get_backup_file_list
142
+ while !all_backup_files.empty? && all_backup_files.length >= @max_backups
143
+ FileUtils.rm(all_backup_files.shift)
144
+ end
145
+ end
146
+
147
+ def next_backup_seq_number
148
+ all_backup_files = self.get_backup_file_list
149
+ return 0 if all_backup_files.empty?
150
+ all_backup_files.map { |f| File.extname(f).scan(/\d+$/).first }.reject(&:nil?).map { |n| n.to_i }.max + 1
151
+ end
152
+ end
153
+ 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Silkedit
4
+ VERSION = "0.1.0"
5
+ end
data/lib/silkedit.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "silkedit/version"
4
+
5
+ module Silkedit
6
+ LIBDIR = File.join(File.dirname(__FILE__), 'silkedit')
7
+ end
8
+
9
+ Dir.glob(File.join(Silkedit::LIBDIR, '**', '*.rb')).each { |file| require file }
data/sig/silkedit.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Silkedit
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end