├── .classpath ├── .gitignore ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── README.md ├── lib ├── CodecJOrbis.jar ├── JOrbisStandalone.jar └── gson-2.8.2.jar ├── release ├── CellPacker.jar ├── CellPacker_Modloader.jar └── CellPacker_old_2.0.jar ├── scripts ├── dark.patch └── time.patch └── src ├── dev └── lb │ └── cellpacker │ ├── CellPackerMain.java │ ├── Logger.java │ ├── NamedRange.java │ ├── TextAreaPrintStream.java │ ├── Utils.java │ ├── annotation │ ├── Async.java │ ├── Calculated.java │ ├── Shortcut.java │ └── Unmodifiable.java │ ├── controls │ ├── CheckBoxList.java │ ├── JAudioPlayer.java │ ├── JDraggableScrollPane.java │ ├── JHistoryTextField.java │ ├── JImageViewer.java │ ├── JSpriteViewer.java │ ├── LaunchWindow.java │ ├── MainWindow.java │ ├── SpriteSavingList.java │ └── TutorialWindow.java │ └── structure │ ├── ByteData.java │ ├── NamedObject.java │ ├── ResourceCategory.java │ ├── ResourceContainer.java │ ├── ResourceFile.java │ ├── ResourceViewManager.java │ ├── Script.java │ ├── SearchableResourceViewManager.java │ ├── ViewCategory.java │ ├── cdb │ ├── CDBFile.java │ ├── Column.java │ ├── Line.java │ ├── ScriptReader.java │ ├── Sheet.java │ └── SheetProps.java │ ├── resource │ ├── AtlasResource.java │ ├── CompoundAtlasResource.java │ ├── FontResource.java │ ├── ImageResource.java │ ├── JsonResource.java │ ├── Resource.java │ └── SoundResource.java │ └── view │ ├── AtlasImageResourceView.java │ ├── CastleDBResourceView.java │ ├── FontResourceView.java │ ├── JsonResourceView.java │ ├── ResourceView.java │ ├── SingleResourceView.java │ └── StaticResourceView.java └── resources └── ico.png /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | CellPacker 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.8 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CellPacker [Outdated] 2 | 3 | **Dead Cells now has its own 4 | (basic) modding tools.** This project is no longer required and might not support newer versions of the game. 5 | 6 | CellPacker can still be used as a resource viewer and editor (unless the res.pak format changes in the future), but can **not** be 7 | used to create mods compatible with the Steam workshop 8 | 9 | ## [Old description below] 10 | 11 | A modding utility for Dead Cells 12 | 13 | Version 2.1 of CellPacker: Information and Help 14 | Please read this file before using the program. 15 | The compiled file can be found [here](https://github.com/ReBuilders101/CellPacker/raw/master/release/CellPacker.jar). 16 | 17 | -------------------------------------------------------------------------------------------------------- 18 | CellPacker is an application to view and modify the resources used by the game Dead Cells. 19 | **NOTE:** The format of the Dead Cells resource file can change at any time and there is no official 20 | documentation. This program was designed to be used with the Baguette Update, but will most 21 | likely also work on newer and older versions. 22 | 23 | -------------------------------------------------------------------------------------------------------- 24 | ### Features: 25 | 26 | 1. View all images/textures of the game that are stored in the resource file 27 | 2. Play all sounds stored in the resource files 28 | 3. Group connected resources into views, so they are easier to find 29 | 4. Export all resources as files (PNG for images, OGG for sounds) 30 | 5. Search for resources 31 | 6. Replace reources with custom ones. This way you can create your own textures or sounds for the game 32 | 7. Re-pack the modified file so it can be used by Dead Cells 33 | 8. ~~A launcher that lets you run scripts to modify the resources, so you can choose if you want to play with modifications without opening the editor~~ 34 | 9. Editing of the game data, either with a text editor or with the external program [CastleDB](http://castledb.org/) (recommended) 35 | 36 | 37 | -------------------------------------------------------------------------------------------------------- 38 | ### How to use: 39 | #### Download 40 | [CellPacker Download](https://github.com/ReBuilders101/CellPacker/raw/master/release/CellPacker.jar) 41 | ##### Editor 42 | 1. CellPacker.jar can be run from any folder. Double-click to execute (Java 8 or newer must be installed) 43 | 2. Use File>Open to open the res.pak file in your Dead Cells folder (usually found at /steamapps/common/Dead Cells/) 44 | 3. On the right, a tree with all found resources will appear. Click on an item to see the resource. 45 | 4. To replace a resource or to export it to a file, select Edit>Resource Options and choose your option. 46 | **IMPORTANT:** When replacing images, make sure the replacement has the same size (pixles, not bytes) as the original. 47 | 5. You can always restore a modified resource to its original state with the Resource Options menu. 48 | 6. To create a version containing your changes that is useable by Dead Cells, simply use File>Save and overwrite the 49 | existing res.pak file. Mare sure to back up the original file before. 50 | 51 | ##### ~~Scripting~~ 52 | The scripting feature was never fully implemented and was made obsolete when modding through the Steam Workshop was introduced 53 | 54 | ##### Uninstall 55 | CellPacker does not create any permanent files. Simply delete the CellPacker.jar file to delete the program from your computer. 56 | 57 | -------------------------------------------------------------------------------------------------------- 58 | ### Compiled files and code: 59 | ##### Compiled files 60 | Use the CellPacker.jar in the release folder. 61 | ##### If you want to compile the code: 62 | This repository contains a full eclipse project, you can just clone and import it. It will probably also work with any other IDE. 63 | All jar files in the `lib` folder are dependencies. 64 | 65 | -------------------------------------------------------------------------------------------------------- 66 | ### License: 67 | Basic: 68 | * Do whatever you want. 69 | 70 | But: 71 | * Please don't claim that you made this. It's not nice. 72 | * Feel free to take any code from this project and use it for your own project. 73 | 74 | Also: 75 | * If Dead Cells breaks, it is not my fault. 76 | 77 | ##### Third Party Licenses and Credits: 78 | This program uses the following third party librarys: 79 | - CodecJOrbis by Paul Lamb (http://paulscode.com), because Java does not support the OGG sound format. 80 | - Gson (under the Apache License 2.0, can be found at http://www.apache.org/licenses/LICENSE-2.0) to parse JSON. 81 | -------------------------------------------------------------------------------- /lib/CodecJOrbis.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReBuilders101/CellPacker/27babca69bdae7b8d9ad7ccc2c1b864853b30632/lib/CodecJOrbis.jar -------------------------------------------------------------------------------- /lib/JOrbisStandalone.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReBuilders101/CellPacker/27babca69bdae7b8d9ad7ccc2c1b864853b30632/lib/JOrbisStandalone.jar -------------------------------------------------------------------------------- /lib/gson-2.8.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReBuilders101/CellPacker/27babca69bdae7b8d9ad7ccc2c1b864853b30632/lib/gson-2.8.2.jar -------------------------------------------------------------------------------- /release/CellPacker.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReBuilders101/CellPacker/27babca69bdae7b8d9ad7ccc2c1b864853b30632/release/CellPacker.jar -------------------------------------------------------------------------------- /release/CellPacker_Modloader.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReBuilders101/CellPacker/27babca69bdae7b8d9ad7ccc2c1b864853b30632/release/CellPacker_Modloader.jar -------------------------------------------------------------------------------- /release/CellPacker_old_2.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReBuilders101/CellPacker/27babca69bdae7b8d9ad7ccc2c1b864853b30632/release/CellPacker_old_2.0.jar -------------------------------------------------------------------------------- /scripts/dark.patch: -------------------------------------------------------------------------------- 1 | { 2 | "add": { 3 | "sheets":[ 4 | { 5 | "CPWHERE": { 6 | "id": "name", 7 | "value": "level" 8 | }, 9 | "lines": [ 10 | { 11 | "CPWHERE": { 12 | "id": "id", 13 | "value": "PrisonStart" 14 | }, 15 | "flags": 64 16 | }, 17 | { 18 | "CPWHERE": { 19 | "id": "id", 20 | "value": "PrisonCourtyard" 21 | }, 22 | "flags": 65 23 | }, 24 | { 25 | "CPWHERE": { 26 | "id": "id", 27 | "value": "PrisonDepths" 28 | }, 29 | "flags": 64 30 | }, 31 | { 32 | "CPWHERE": { 33 | "id": "id", 34 | "value": "SewerShort" 35 | }, 36 | "flags": 72 37 | }, 38 | { 39 | "CPWHERE": { 40 | "id": "id", 41 | "value": "PrisonStart" 42 | }, 43 | "flags": 64 44 | }, 45 | { 46 | "CPWHERE": { 47 | "id": "id", 48 | "value": "PrisonRoof" 49 | }, 50 | "flags": 65 51 | }, 52 | { 53 | "CPWHERE": { 54 | "id": "id", 55 | "value": "SewerDepths" 56 | }, 57 | "flags": 72 58 | }, 59 | { 60 | "CPWHERE": { 61 | "id": "id", 62 | "value": "Ossuary" 63 | }, 64 | "flags": 64 65 | }, 66 | { 67 | "CPWHERE": { 68 | "id": "id", 69 | "value": "StiltVillage" 70 | }, 71 | "flags": 325 72 | }, 73 | { 74 | "CPWHERE": { 75 | "id": "id", 76 | "value": "AncientTemple" 77 | }, 78 | "flags": 64 79 | }, 80 | { 81 | "CPWHERE": { 82 | "id": "id", 83 | "value": "Cemetery" 84 | }, 85 | "flags": 65 86 | }, 87 | { 88 | "CPWHERE": { 89 | "id": "id", 90 | "value": "ClockTower" 91 | }, 92 | "flags": 192 93 | } 94 | ] 95 | } 96 | , 97 | { 98 | "CPWHERE": { 99 | "id": "name", 100 | "value": "room" 101 | }, 102 | "lines": [ 103 | { 104 | "CPWHERE": { 105 | "id": "id", 106 | "value": "PrisonFlaskRoom" 107 | }, 108 | "markers": [ 109 | { 110 | "x": 28, 111 | "y": 30, 112 | "marker": "DarknessRemover", 113 | "width": 1, 114 | "height": 1, 115 | "customId": "-7" 116 | }, 117 | { 118 | "x": 93, 119 | "y": 28, 120 | "marker": "DarknessRemover", 121 | "width": 1, 122 | "height": 1, 123 | "customId": "-7" 124 | }, 125 | { 126 | "x": 83, 127 | "y": 26, 128 | "marker": "DarknessRemover", 129 | "width": 1, 130 | "height": 1, 131 | "customId": "-7" 132 | }, 133 | { 134 | "x": 51, 135 | "y": 26, 136 | "marker": "DarknessRemover", 137 | "width": 1, 138 | "height": 1, 139 | "customId": "-7" 140 | } 141 | ] 142 | }, 143 | { 144 | "CPWHERE": { 145 | "id": "id", 146 | "value": "Exit_LR" 147 | }, 148 | "markers": [ 149 | { 150 | "x": 3, 151 | "y": 7, 152 | "marker": "DarknessRemover", 153 | "width": 1, 154 | "height": 1, 155 | "customId": "-7" 156 | }, 157 | { 158 | "x": 11, 159 | "y": 7, 160 | "marker": "DarknessRemover", 161 | "width": 1, 162 | "height": 1, 163 | "customId": "-7" 164 | } 165 | ] 166 | }, 167 | { 168 | "CPWHERE": { 169 | "id": "id", 170 | "value": "Exit_LDR" 171 | }, 172 | "markers": [ 173 | { 174 | "x": 6, 175 | "y": 6, 176 | "marker": "DarknessRemover", 177 | "width": 1, 178 | "height": 1, 179 | "customId": "-7" 180 | }, 181 | { 182 | "x": 16, 183 | "y": 6, 184 | "marker": "DarknessRemover", 185 | "width": 1, 186 | "height": 1, 187 | "customId": "-7" 188 | } 189 | ] 190 | }, 191 | { 192 | "CPWHERE": { 193 | "id": "id", 194 | "value": "Exit_UL" 195 | }, 196 | "markers": [ 197 | { 198 | "x": 7, 199 | "y": 5, 200 | "marker": "DarknessRemover", 201 | "width": 1, 202 | "height": 1, 203 | "customId": "-7" 204 | } 205 | ] 206 | }, 207 | { 208 | "CPWHERE": { 209 | "id": "id", 210 | "value": "Exit_DL" 211 | }, 212 | "markers": [ 213 | { 214 | "x": 9, 215 | "y": 11, 216 | "marker": "DarknessRemover", 217 | "width": 1, 218 | "height": 1, 219 | "customId": "-7" 220 | } 221 | ] 222 | }, 223 | { 224 | "CPWHERE": { 225 | "id": "id", 226 | "value": "BasicEntrance_R" 227 | }, 228 | "markers": [ 229 | { 230 | "x": 22, 231 | "y": 8, 232 | "marker": "DarknessRemover", 233 | "width": 1, 234 | "height": 1, 235 | "customId": "-7" 236 | } 237 | ] 238 | }, 239 | { 240 | "CPWHERE": { 241 | "id": "id", 242 | "value": "BasicEntrance_D" 243 | }, 244 | "markers": [ 245 | { 246 | "x": 14, 247 | "y": 12, 248 | "marker": "DarknessRemover", 249 | "width": 1, 250 | "height": 1, 251 | "customId": "-7" 252 | } 253 | ] 254 | }, 255 | { 256 | "CPWHERE": { 257 | "id": "id", 258 | "value": "BasicEntrance_U" 259 | }, 260 | "markers": [ 261 | { 262 | "x": 17, 263 | "y": 7, 264 | "marker": "DarknessRemover", 265 | "width": 1, 266 | "height": 1, 267 | "customId": "-7" 268 | } 269 | ] 270 | }, 271 | { 272 | "CPWHERE": { 273 | "id": "id", 274 | "value": "OutsideEntrance" 275 | }, 276 | "markers": [ 277 | { 278 | "x": 22, 279 | "y": 14, 280 | "marker": "DarknessRemover", 281 | "width": 1, 282 | "height": 1, 283 | "customId": "-7" 284 | } 285 | ] 286 | }, 287 | { 288 | "CPWHERE": { 289 | "id": "id", 290 | "value": "PrisonStartExit" 291 | }, 292 | "markers": [ 293 | { 294 | "x": 26, 295 | "y": 5, 296 | "marker": "DarknessRemover", 297 | "width": 1, 298 | "height": 1, 299 | "customId": "-7" 300 | } 301 | ] 302 | }, 303 | { 304 | "CPWHERE": { 305 | "id": "id", 306 | "value": "PrisonMonsterDoor" 307 | }, 308 | "markers": [ 309 | { 310 | "x": 2, 311 | "y": 10, 312 | "marker": "DarknessRemover", 313 | "width": 1, 314 | "height": 1, 315 | "customId": "5" 316 | }, 317 | { 318 | "x": 10, 319 | "y": 10, 320 | "marker": "DarknessRemover", 321 | "width": 1, 322 | "height": 1, 323 | "customId": "5" 324 | } 325 | ] 326 | }, 327 | { 328 | "CPWHERE": { 329 | "id": "id", 330 | "value": "PrisonDepthsEntrance" 331 | }, 332 | "markers": [ 333 | { 334 | "x": 22, 335 | "y": 16, 336 | "marker": "DarknessRemover", 337 | "width": 1, 338 | "height": 1, 339 | "customId": "-7" 340 | } 341 | ] 342 | }, 343 | { 344 | "CPWHERE": { 345 | "id": "id", 346 | "value": "PrisonHubEntrance" 347 | }, 348 | "markers": [ 349 | { 350 | "x": 19, 351 | "y": 17, 352 | "marker": "DarknessRemover", 353 | "width": 1, 354 | "height": 1, 355 | "customId": "-7" 356 | } 357 | ] 358 | }, 359 | { 360 | "CPWHERE": { 361 | "id": "id", 362 | "value": "RoofEntrance" 363 | }, 364 | "markers": [ 365 | { 366 | "x": 23, 367 | "y": 8, 368 | "marker": "DarknessRemover", 369 | "width": 1, 370 | "height": 1, 371 | "customId": "5" 372 | }, 373 | { 374 | "x": 18, 375 | "y": 28, 376 | "marker": "DarknessRemover", 377 | "width": 1, 378 | "height": 1, 379 | "customId": "-7" 380 | } 381 | ] 382 | }, 383 | { 384 | "CPWHERE": { 385 | "id": "id", 386 | "value": "RoofEndSubRoom" 387 | }, 388 | "markers": [ 389 | { 390 | "x": 9, 391 | "y": 18, 392 | "marker": "DarknessRemover", 393 | "width": 1, 394 | "height": 1, 395 | "customId": "-7" 396 | } 397 | ] 398 | }, 399 | { 400 | "CPWHERE": { 401 | "id": "id", 402 | "value": "RoofEndNothing" 403 | }, 404 | "markers": [ 405 | { 406 | "x": 9, 407 | "y": 18, 408 | "marker": "DarknessRemover", 409 | "width": 1, 410 | "height": 1, 411 | "customId": "-7" 412 | } 413 | ] 414 | }, 415 | { 416 | "CPWHERE": { 417 | "id": "id", 418 | "value": "RoofEndBridge" 419 | }, 420 | "markers": [ 421 | { 422 | "x": 9, 423 | "y": 15, 424 | "marker": "DarknessRemover", 425 | "width": 1, 426 | "height": 1, 427 | "customId": "5" 428 | }, 429 | { 430 | "x": 39, 431 | "y": 16, 432 | "marker": "DarknessRemover", 433 | "width": 1, 434 | "height": 1, 435 | "customId": "-7" 436 | } 437 | ] 438 | }, 439 | { 440 | "CPWHERE": { 441 | "id": "id", 442 | "value": "RoofEndExit" 443 | }, 444 | "markers": [ 445 | { 446 | "x": 13, 447 | "y": 26, 448 | "marker": "DarknessRemover", 449 | "width": 1, 450 | "height": 1, 451 | "customId": "5" 452 | }, 453 | { 454 | "x": 23, 455 | "y": 108, 456 | "marker": "DarknessRemover", 457 | "width": 1, 458 | "height": 1, 459 | "customId": "-7" 460 | } 461 | ] 462 | }, 463 | { 464 | "CPWHERE": { 465 | "id": "id", 466 | "value": "RoofInnerExit" 467 | }, 468 | "markers": [ 469 | { 470 | "x": 14, 471 | "y": 5, 472 | "marker": "DarknessRemover", 473 | "width": 1, 474 | "height": 1, 475 | "customId": "-7" 476 | } 477 | ] 478 | }, 479 | { 480 | "CPWHERE": { 481 | "id": "id", 482 | "value": "SewerShortEntrance" 483 | }, 484 | "markers": [ 485 | { 486 | "x": 13, 487 | "y": 5, 488 | "marker": "DarknessRemover", 489 | "width": 1, 490 | "height": 1, 491 | "customId": "5" 492 | }, 493 | { 494 | "x": 25, 495 | "y": 12, 496 | "marker": "DarknessRemover", 497 | "width": 1, 498 | "height": 1, 499 | "customId": "-7" 500 | } 501 | ] 502 | }, 503 | { 504 | "CPWHERE": { 505 | "id": "id", 506 | "value": "SewerDepthsEntrance" 507 | }, 508 | "markers": [ 509 | { 510 | "x": 24, 511 | "y": 9, 512 | "marker": "DarknessRemover", 513 | "width": 1, 514 | "height": 1, 515 | "customId": "5" 516 | }, 517 | { 518 | "x": 32, 519 | "y": 23, 520 | "marker": "DarknessRemover", 521 | "width": 1, 522 | "height": 1, 523 | "customId": "-7" 524 | } 525 | ] 526 | }, 527 | { 528 | "CPWHERE": { 529 | "id": "id", 530 | "value": "SewerCreature" 531 | }, 532 | "markers": [ 533 | { 534 | "x": 12, 535 | "y": 6, 536 | "marker": "DarknessRemover", 537 | "width": 1, 538 | "height": 1, 539 | "customId": "5" 540 | }, 541 | { 542 | "x": 31, 543 | "y": 6, 544 | "marker": "DarknessRemover", 545 | "width": 1, 546 | "height": 1, 547 | "customId": "-7" 548 | } 549 | ] 550 | }, 551 | { 552 | "CPWHERE": { 553 | "id": "id", 554 | "value": "OssEntrance" 555 | }, 556 | "markers": [ 557 | { 558 | "x": 18, 559 | "y": 24, 560 | "marker": "DarknessRemover", 561 | "width": 1, 562 | "height": 1, 563 | "customId": "5" 564 | }, 565 | { 566 | "x": 45, 567 | "y": 28, 568 | "marker": "DarknessRemover", 569 | "width": 1, 570 | "height": 1, 571 | "customId": "-7" 572 | } 573 | ] 574 | }, 575 | { 576 | "CPWHERE": { 577 | "id": "id", 578 | "value": "SVEntrance" 579 | }, 580 | "markers": [ 581 | { 582 | "x": 8, 583 | "y": 20, 584 | "marker": "DarknessRemover", 585 | "width": 1, 586 | "height": 1, 587 | "customId": "-7" 588 | } 589 | ] 590 | }, 591 | { 592 | "CPWHERE": { 593 | "id": "id", 594 | "value": "SVEnd" 595 | }, 596 | "markers": [ 597 | { 598 | "x": 28, 599 | "y": 26, 600 | "marker": "DarknessRemover", 601 | "width": 1, 602 | "height": 1, 603 | "customId": "-7" 604 | }, 605 | { 606 | "x": 12, 607 | "y": 80, 608 | "marker": "DarknessRemover", 609 | "width": 1, 610 | "height": 1, 611 | "customId": "-7" 612 | }, 613 | { 614 | "x": 10, 615 | "y": 90, 616 | "marker": "DarknessRemover", 617 | "width": 1, 618 | "height": 1, 619 | "customId": "-7" 620 | }, 621 | { 622 | "x": 31, 623 | "y": 90, 624 | "marker": "DarknessRemover", 625 | "width": 1, 626 | "height": 1, 627 | "customId": "-7" 628 | }, 629 | { 630 | "x": 59, 631 | "y": 90, 632 | "marker": "DarknessRemover", 633 | "width": 1, 634 | "height": 1, 635 | "customId": "-7" 636 | } 637 | ] 638 | }, 639 | { 640 | "CPWHERE": { 641 | "id": "id", 642 | "value": "SVInteriorExit" 643 | }, 644 | "markers": [ 645 | { 646 | "x": 22, 647 | "y": 40, 648 | "marker": "DarknessRemover", 649 | "width": 1, 650 | "height": 1, 651 | "customId": "-7" 652 | } 653 | ] 654 | }, 655 | { 656 | "CPWHERE": { 657 | "id": "id", 658 | "value": "SVMidGate" 659 | }, 660 | "markers": [ 661 | { 662 | "x": 11, 663 | "y": 52, 664 | "marker": "DarknessRemover", 665 | "width": 1, 666 | "height": 1, 667 | "customId": "5" 668 | }, 669 | { 670 | "x": 8, 671 | "y": 87, 672 | "marker": "DarknessRemover", 673 | "width": 1, 674 | "height": 1, 675 | "customId": "-7" 676 | }, 677 | { 678 | "x": 14, 679 | "y": 87, 680 | "marker": "DarknessRemover", 681 | "width": 1, 682 | "height": 1, 683 | "customId": "-7" 684 | } 685 | ] 686 | }, 687 | { 688 | "CPWHERE": { 689 | "id": "id", 690 | "value": "CEntrance" 691 | }, 692 | "markers": [ 693 | { 694 | "x": 7, 695 | "y": 27, 696 | "marker": "DarknessRemover", 697 | "width": 1, 698 | "height": 1, 699 | "customId": "5" 700 | }, 701 | { 702 | "x": 14, 703 | "y": 96, 704 | "marker": "DarknessRemover", 705 | "width": 1, 706 | "height": 1, 707 | "customId": "-7" 708 | }, 709 | { 710 | "x": 30, 711 | "y": 96, 712 | "marker": "DarknessRemover", 713 | "width": 1, 714 | "height": 1, 715 | "customId": "-7" 716 | } 717 | ] 718 | }, 719 | { 720 | "CPWHERE": { 721 | "id": "id", 722 | "value": "CEndExit" 723 | }, 724 | "markers": [ 725 | { 726 | "x": 22, 727 | "y": 27, 728 | "marker": "DarknessRemover", 729 | "width": 1, 730 | "height": 1, 731 | "customId": "5" 732 | }, 733 | { 734 | "x": 8, 735 | "y": 83, 736 | "marker": "DarknessRemover", 737 | "width": 1, 738 | "height": 1, 739 | "customId": "-7" 740 | }, 741 | { 742 | "x": 33, 743 | "y": 83, 744 | "marker": "DarknessRemover", 745 | "width": 1, 746 | "height": 1, 747 | "customId": "-7" 748 | } 749 | ] 750 | }, 751 | { 752 | "CPWHERE": { 753 | "id": "id", 754 | "value": "CLadderGateDone" 755 | }, 756 | "markers": [ 757 | { 758 | "x": 15, 759 | "y": 15, 760 | "marker": "DarknessRemover", 761 | "width": 1, 762 | "height": 1, 763 | "customId": "-7" 764 | } 765 | ] 766 | }, 767 | { 768 | "CPWHERE": { 769 | "id": "id", 770 | "value": "CemEntrance" 771 | }, 772 | "markers": [ 773 | { 774 | "x": 26, 775 | "y": 48, 776 | "marker": "DarknessRemover", 777 | "width": 1, 778 | "height": 1, 779 | "customId": "-7" 780 | } 781 | ] 782 | }, 783 | { 784 | "CPWHERE": { 785 | "id": "id", 786 | "value": "CemEnd" 787 | }, 788 | "markers": [ 789 | { 790 | "x": 13, 791 | "y": 71, 792 | "marker": "DarknessRemover", 793 | "width": 1, 794 | "height": 1, 795 | "customId": "-7" 796 | } 797 | ] 798 | }, 799 | { 800 | "CPWHERE": { 801 | "id": "id", 802 | "value": "CemBigCrypt" 803 | }, 804 | "markers": [ 805 | { 806 | "x": 7, 807 | "y": 22, 808 | "marker": "DarknessRemover", 809 | "width": 1, 810 | "height": 1, 811 | "customId": "5" 812 | }, 813 | { 814 | "x": 48, 815 | "y": 22, 816 | "marker": "DarknessRemover", 817 | "width": 1, 818 | "height": 1, 819 | "customId": "-7" 820 | }, 821 | { 822 | "x": 18, 823 | "y": 38, 824 | "marker": "DarknessRemover", 825 | "width": 1, 826 | "height": 1, 827 | "customId": "-7" 828 | } 829 | ] 830 | }, 831 | { 832 | "CPWHERE": { 833 | "id": "id", 834 | "value": "ClockTowerEntrance" 835 | }, 836 | "markers": [ 837 | { 838 | "x": 49, 839 | "y": 13, 840 | "marker": "DarknessRemover", 841 | "width": 1, 842 | "height": 1, 843 | "customId": "5" 844 | }, 845 | { 846 | "x": 33, 847 | "y": 31, 848 | "marker": "DarknessRemover", 849 | "width": 1, 850 | "height": 1, 851 | "customId": "-7" 852 | }, 853 | { 854 | "x": 53, 855 | "y": 31, 856 | "marker": "DarknessRemover", 857 | "width": 1, 858 | "height": 1, 859 | "customId": "-7" 860 | } 861 | ] 862 | }, 863 | { 864 | "CPWHERE": { 865 | "id": "id", 866 | "value": "ClockTowerExit" 867 | }, 868 | "markers": [ 869 | { 870 | "x": 4, 871 | "y": 8, 872 | "marker": "DarknessRemover", 873 | "width": 1, 874 | "height": 1, 875 | "customId": "-7" 876 | } 877 | ] 878 | }, 879 | { 880 | "CPWHERE": { 881 | "id": "id", 882 | "value": "AncientEntrance" 883 | }, 884 | "markers": [ 885 | { 886 | "x": 20, 887 | "y": 14, 888 | "marker": "DarknessRemover", 889 | "width": 1, 890 | "height": 1, 891 | "customId": "-7" 892 | } 893 | ] 894 | } 895 | ] 896 | } 897 | ] 898 | } 899 | } -------------------------------------------------------------------------------- /scripts/time.patch: -------------------------------------------------------------------------------- 1 | { 2 | "add": { 3 | "sheets":[ 4 | { 5 | "CPWHERE": { 6 | "id": "name", "value": "room" 7 | } 8 | , 9 | "lines": [ 10 | { 11 | "CPWHERE": { 12 | "id": "id", 13 | "value": "PrisonFlaskRoom" 14 | } 15 | , 16 | "markers": [ 17 | { 18 | "x": 85, 19 | "y": 26, 20 | "marker": "FixedLoot", 21 | "width": 1, 22 | "height": 1, 23 | "item": "TimeDistorsion" 24 | }, 25 | { 26 | "x": 83, 27 | "y": 26, 28 | "marker": "FixedBlueprint", 29 | "width": 1, 30 | "height": 1, 31 | "item": "TimeDistorsion" 32 | } 33 | ] 34 | } 35 | ] 36 | }, 37 | { 38 | "CPWHERE": { 39 | "id": "name", "value": "item" 40 | } 41 | , 42 | "lines": [ 43 | { 44 | "CPWHERE": { 45 | "id": "id", "value": "TimeDistorsion" 46 | } 47 | , 48 | "droppable": true, 49 | "hasLevels": true, 50 | "cellCost": 1 51 | } 52 | ] 53 | } 54 | ] 55 | } 56 | } -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/CellPackerMain.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker; 2 | 3 | import java.io.File; 4 | 5 | import javax.swing.ImageIcon; 6 | import javax.swing.UIManager; 7 | import javax.swing.UnsupportedLookAndFeelException; 8 | 9 | import dev.lb.cellpacker.controls.LaunchWindow; 10 | import dev.lb.cellpacker.controls.MainWindow; 11 | 12 | public class CellPackerMain { 13 | 14 | public static final boolean MODLOADER = false; 15 | 16 | public static boolean ASK_RESOURCE_RESTORE = true; 17 | public static File CHOOSE_ROOT_FILE = new File("."); 18 | 19 | public static MainWindow mainFrame; 20 | 21 | public static void main(String[] args) { 22 | try { 23 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 24 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException 25 | | UnsupportedLookAndFeelException e1) { 26 | Logger.printWarning("CellPackerMain.main()", "Could not set L&F for this platform"); 27 | } 28 | 29 | if(MODLOADER){ 30 | LaunchWindow lw = new LaunchWindow(false); 31 | lw.setVisible(true); 32 | lw.setIconImage(new ImageIcon(CellPackerMain.class.getResource("/resources/ico.png")).getImage()); 33 | }else{ 34 | mainFrame = new MainWindow(); 35 | mainFrame.setVisible(true); 36 | } 37 | } 38 | 39 | 40 | @SuppressWarnings("unused") 41 | private static String concat(String[] array){ 42 | if(array == null) return null; 43 | if(array.length == 0) return null; 44 | String concat = ""; 45 | for(String s : array){ 46 | concat = concat + s + " "; 47 | } 48 | return concat; 49 | } 50 | 51 | public static MainWindow getMainFrame(){ 52 | return mainFrame; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/Logger.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker; 2 | 3 | public final class Logger { 4 | private Logger(){} 5 | 6 | public static void throwFatal(Throwable ex, int exitCode){ 7 | System.err.println("[ErrorHandler]: A fatal error has occurred:"); 8 | ex.printStackTrace(); 9 | System.exit(exitCode); 10 | } 11 | 12 | public static void throwFatal(Throwable ex){ 13 | throwFatal(ex, 1); 14 | } 15 | 16 | public static void printWarning(String trace, String warning){ 17 | System.out.println("[WARNING|" + trace + "]: " + warning); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/NamedRange.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker; 2 | 3 | public class NamedRange{ 4 | 5 | private String key; 6 | private int start; 7 | private int end; 8 | 9 | public NamedRange(String name, int start) { 10 | this.key = name; 11 | this.start = start; 12 | } 13 | 14 | public String getName() { 15 | return key; 16 | } 17 | 18 | public int getStart() { 19 | return start; 20 | } 21 | 22 | public int getEnd(){ 23 | return end; 24 | } 25 | 26 | public int getSize(){ 27 | return end - start; 28 | } 29 | 30 | public void setEnd(int value) { 31 | end = value; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "NamedRange [name=" + key + ", start=" + start + ", end=" + end + "]"; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/TextAreaPrintStream.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | 6 | import javax.swing.JTextArea; 7 | 8 | public class TextAreaPrintStream extends PrintStream{ 9 | 10 | private JTextArea textArea; 11 | 12 | public TextAreaPrintStream(JTextArea area){ 13 | super(new ByteArrayOutputStream(), true); 14 | textArea = area; 15 | 16 | } 17 | 18 | @Override 19 | public void flush() { 20 | super.flush(); 21 | textArea.setText(new String(((ByteArrayOutputStream) out).toByteArray())); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/Utils.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Component; 5 | import java.awt.Container; 6 | import java.awt.Desktop; 7 | import java.awt.Dimension; 8 | import java.awt.FlowLayout; 9 | import java.awt.LayoutManager; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.io.Reader; 13 | import java.io.StringReader; 14 | import java.net.URI; 15 | import java.net.URISyntaxException; 16 | import java.util.function.Consumer; 17 | import java.util.function.Predicate; 18 | import java.util.function.Supplier; 19 | 20 | import javax.swing.BorderFactory; 21 | import javax.swing.JComponent; 22 | import javax.swing.JOptionPane; 23 | import javax.swing.JPanel; 24 | import javax.swing.JProgressBar; 25 | import javax.swing.JTextArea; 26 | 27 | import com.google.gson.JsonArray; 28 | import com.google.gson.JsonElement; 29 | import com.google.gson.JsonObject; 30 | import com.google.gson.stream.JsonReader; 31 | import com.google.gson.stream.JsonToken; 32 | import com.google.gson.stream.MalformedJsonException; 33 | 34 | 35 | public final class Utils { 36 | private Utils(){} 37 | 38 | public static T setWidth(T control, int width){ 39 | control.setPreferredSize(new Dimension(width, control.getPreferredSize().height)); 40 | return control; 41 | } 42 | 43 | public static T setMaxWidth(T control){ 44 | control.setMaximumSize(new Dimension(Integer.MAX_VALUE, control.getMaximumSize().height)); 45 | return control; 46 | } 47 | 48 | public static T call(T object, Consumer action){ 49 | action.accept(object); 50 | return object; 51 | } 52 | 53 | public static T setHeight(T control, int height){ 54 | control.setPreferredSize(new Dimension(control.getPreferredSize().width, height)); 55 | return control; 56 | } 57 | 58 | public static T setPrefSize(T control, int width, int height){ 59 | control.setPreferredSize(new Dimension(width, height)); 60 | return control; 61 | } 62 | 63 | public static JPanel createGroupBox(String title){ 64 | JPanel pan = new JPanel(); 65 | pan.setBorder(BorderFactory.createTitledBorder(title)); 66 | return pan; 67 | } 68 | 69 | public static JPanel createGroupBox(String title, LayoutManager layout){ 70 | JPanel pan = new JPanel(layout); 71 | pan.setBorder(BorderFactory.createTitledBorder(title)); 72 | return pan; 73 | } 74 | 75 | public static T addAll(T container, Component...components){ 76 | for(Component c : components){ 77 | container.add(c); 78 | } 79 | return container; 80 | } 81 | 82 | public static JTextArea getTextDisplay(String text){ 83 | JTextArea txt = new JTextArea(text); 84 | txt.setEditable(false); 85 | return txt; 86 | } 87 | 88 | public static JProgressBar getWaitingBar(int width){ 89 | JProgressBar pro = new JProgressBar(); 90 | pro.setIndeterminate(true); 91 | return setWidth(pro, width); 92 | } 93 | 94 | public static Container asyncFill(Supplier content, int waitBarWidth){ 95 | JPanel con = new JPanel(); 96 | JProgressBar wait = getWaitingBar(waitBarWidth); 97 | con.add(wait); 98 | new Thread(() -> { 99 | Component com = content.get(); 100 | con.remove(wait); 101 | con.setLayout(new BorderLayout()); 102 | con.add(com); 103 | con.revalidate(); 104 | }).start(); 105 | return con; 106 | } 107 | 108 | public static Container asyncFill(Supplier content, int waitBarWidth, Object constraints){ 109 | JPanel con = new JPanel(); 110 | JProgressBar wait = getWaitingBar(waitBarWidth); 111 | con.add(wait, constraints); 112 | new Thread(() -> { 113 | Component com = content.get(); 114 | con.remove(wait); 115 | con.setLayout(new BorderLayout()); 116 | con.add(com, constraints); 117 | con.revalidate(); 118 | }).start(); 119 | return con; 120 | } 121 | 122 | public static JPanel pack(Component...components){ 123 | JPanel container = new JPanel(new FlowLayout()); 124 | for(Component c : components){ 125 | container.add(c); 126 | } 127 | return container; 128 | } 129 | 130 | 131 | //Validation methods from SO 132 | public static boolean isJsonValid(final String json) { 133 | return isJsonValid(new StringReader(json)); 134 | } 135 | 136 | private static boolean isJsonValid(final Reader reader) { 137 | return isJsonValid(new JsonReader(reader)); 138 | } 139 | 140 | private static boolean isJsonValid(final JsonReader jsonReader) { 141 | try { 142 | JsonToken token; 143 | loop: 144 | while ( (token = jsonReader.peek()) != JsonToken.END_DOCUMENT && token != null ) { 145 | switch ( token ) { 146 | case BEGIN_ARRAY: 147 | jsonReader.beginArray(); 148 | break; 149 | case END_ARRAY: 150 | jsonReader.endArray(); 151 | break; 152 | case BEGIN_OBJECT: 153 | jsonReader.beginObject(); 154 | break; 155 | case END_OBJECT: 156 | jsonReader.endObject(); 157 | break; 158 | case NAME: 159 | jsonReader.nextName(); 160 | break; 161 | case STRING: 162 | case NUMBER: 163 | case BOOLEAN: 164 | case NULL: 165 | jsonReader.skipValue(); 166 | break; 167 | case END_DOCUMENT: 168 | break loop; 169 | default: 170 | throw new AssertionError(token); 171 | } 172 | } 173 | return true; 174 | } catch ( final MalformedJsonException ignored ) { 175 | return false; 176 | } catch (IOException | AssertionError e) { 177 | e.printStackTrace(); 178 | return false; 179 | } 180 | } 181 | 182 | public static void showAboutDialog(String text, String title, String url){ 183 | int result = JOptionPane.showOptionDialog(CellPackerMain.getMainFrame(), text, title, JOptionPane.DEFAULT_OPTION, 184 | JOptionPane.INFORMATION_MESSAGE, null, new String[]{"Go to website", "Close"}, 0); 185 | if(result == 1) return; 186 | try { 187 | Desktop.getDesktop().browse(new URI(url)); 188 | } catch (IOException | URISyntaxException e) { 189 | e.printStackTrace(); 190 | } 191 | } 192 | 193 | public static void addJSON(JsonObject base, JsonObject add){ 194 | //Iterate over all entries in add 195 | for(String addKey : add.keySet()){ 196 | //get the current entry as an object 197 | JsonElement addElement = add.get(addKey); 198 | //If the base tag does not have this tag yet, add this tag to base 199 | if(!base.has(addKey)){ 200 | base.add(addKey, addElement); 201 | }else{ //If it does exist, try to merge depending on the type 202 | addJsonByType(base.get(addKey), addElement, (e) -> base.add(addKey, e)); 203 | } 204 | } 205 | } 206 | 207 | private static void addJsonByType(JsonElement baseElement, JsonElement addElement, Consumer overwriteAction){ 208 | //The simplest is a primitive, it is just replaced: 209 | if(baseElement.isJsonPrimitive() && addElement.isJsonPrimitive()){ 210 | overwriteAction.accept(addElement); 211 | //If they are both objects, recursively call this merge method 212 | }else if(baseElement.isJsonObject() && addElement.isJsonObject()){ 213 | addJSON(baseElement.getAsJsonObject(), addElement.getAsJsonObject()); 214 | //If they are arrays, find markers or append 215 | }else if(baseElement.isJsonArray() && addElement.isJsonArray()){ 216 | //save the elements in new variables 217 | JsonArray addArray = addElement.getAsJsonArray(); 218 | JsonArray baseArray = baseElement.getAsJsonArray(); 219 | //Then iterate over the added array 220 | for(JsonElement addArrayElement : addArray){ 221 | //Handle depending on type 222 | //If it is an object, look for markers 223 | if(addArrayElement.isJsonObject()){ 224 | //Check for CPWHERE (overrides CPINDEX) and if it is an object 225 | if(addArrayElement.getAsJsonObject().has("CPWHERE") && 226 | addArrayElement.getAsJsonObject().get("CPWHERE").isJsonObject()){ 227 | JsonObject whereObject = addArrayElement.getAsJsonObject().get("CPWHERE").getAsJsonObject(); 228 | //Test if where has the Strings 'id' and 'value' 229 | if(whereObject.has("id") && whereObject.get("id").isJsonPrimitive() && 230 | whereObject.has("value") && whereObject.get("value").isJsonPrimitive()){ 231 | String id = whereObject.get("id").getAsString(); 232 | String value = whereObject.get("value").getAsString(); 233 | //Now find an entry in the base array that has id==value 234 | for(int i = 0; i < baseArray.size(); i++){ 235 | JsonElement baseArrayElement = baseArray.get(i); 236 | //The element has to be an object and has to have a tag called id that is a primitive 237 | if(baseArrayElement.isJsonObject() && baseArrayElement.getAsJsonObject().has(id) && 238 | baseArrayElement.getAsJsonObject().get(id).isJsonPrimitive()){ 239 | //Value has to be equal 240 | if(baseArrayElement.getAsJsonObject().get(id).getAsString().equals(value)){ 241 | //We found a tag 242 | //Now remove the CPWHERE part 243 | addArrayElement.getAsJsonObject().remove("CPWHERE"); 244 | final int index = i; //This is necessary, lambda doesn't like non-finals 245 | addJsonByType(baseArrayElement, addArrayElement, (e) -> baseArray.set(index, e)); 246 | //Then break out of the for loop, only the first item should be modified 247 | break; 248 | } 249 | } 250 | } 251 | }else{ 252 | System.err.println("Json Parser - Add: Found CPWHERE tag, but 'id' or 'value' tags were missing"); 253 | } 254 | //if the reqired fields do not exist, do nothing 255 | 256 | //Check for CPINDEX next and if it is a primitive 257 | }else if(addArrayElement.getAsJsonObject().has("CPINDEX") && 258 | addArrayElement.getAsJsonObject().get("CPINDEX").isJsonPrimitive()){ 259 | //Check if the index is within range 260 | int index = addArrayElement.getAsJsonObject().get("CPINDEX").getAsInt(); 261 | if(index >= 0 && index < baseArray.size()){ 262 | //Get the element from the base array 263 | JsonElement baseArrayElement = baseArray.get(index); 264 | //Also remove the CPINDEX tag before merging 265 | addArrayElement.getAsJsonObject().remove("CPINDEX"); 266 | addJsonByType(baseArrayElement, addArrayElement, (e) -> baseArray.set(index, e)); 267 | }else{ 268 | System.err.println("Json Parser - Add: Found CPINDEX tag, but index was out of bounds"); 269 | } 270 | //If the index is out of bounds, do nothing 271 | //If no marker is found, simply append 272 | }else{ 273 | baseArray.add(addArrayElement); 274 | } 275 | //Primitives and arrays (can you even put arrays in arrays in json?) cannot have a marker and are appended 276 | }else{ 277 | baseArray.add(addArrayElement); 278 | } 279 | } 280 | //If they are different types, they are also replaced 281 | }else{ 282 | overwriteAction.accept(addElement); 283 | } 284 | } 285 | 286 | 287 | private static void removeJsonByType(JsonElement baseElement, JsonElement removeElement, Runnable deleteAction){ 288 | //Basic behavior: if any is primitive, delete 289 | //If both are objects, recursive call removeJson 290 | //If both are arrays, go through entries and apply this 291 | //If they are different types, delete 292 | if(baseElement.isJsonPrimitive() && removeElement.isJsonPrimitive()){ 293 | deleteAction.run(); 294 | }else if(baseElement.isJsonObject() && removeElement.isJsonObject()){ 295 | removeJSON(baseElement.getAsJsonObject(), removeElement.getAsJsonObject()); 296 | }else if(baseElement.isJsonArray() && removeElement.isJsonArray()){ 297 | //Iterate over the remove array entries 298 | JsonArray removeArray = removeElement.getAsJsonArray(); 299 | JsonArray baseArray = baseElement.getAsJsonArray(); 300 | for(JsonElement removeArrayElement : removeArray){ 301 | if(removeArrayElement.isJsonObject()){ 302 | if(removeArrayElement.getAsJsonObject().has("CPINDEX") && 303 | removeArrayElement.getAsJsonObject().get("CPINDEX").isJsonPrimitive()){ 304 | final int index = removeArrayElement.getAsJsonObject().get("CPINDEX").getAsInt(); 305 | if(index >= 0 && index < baseArray.size()){ 306 | removeJsonByType(baseArray.get(index), removeArrayElement, () -> baseArray.remove(index)); 307 | }else{ 308 | System.err.println("Json Parser - Remove: Found CPINDEX tag, but index was out of bounds"); 309 | } 310 | }else if(removeArrayElement.getAsJsonObject().has("CPWHERE") && 311 | removeArrayElement.getAsJsonObject().get("CPWHERE").isJsonObject()){ 312 | JsonObject whereObject = removeArrayElement.getAsJsonObject().get("CPWHERE").getAsJsonObject(); 313 | if(whereObject.has("id") && whereObject.get("id").isJsonPrimitive() && 314 | whereObject.has("value") && whereObject.get("value").isJsonPrimitive()){ 315 | String id = whereObject.get("id").getAsString(); 316 | String value = whereObject.get("value").getAsString(); 317 | //Now iterate over basearray and find tag to remove 318 | for(int i = 0; i < baseArray.size(); i++){ 319 | JsonElement baseArrayElement = baseArray.get(i); 320 | //can only delete objects bc only objects can have id==value 321 | if(baseArrayElement.isJsonObject() && baseArrayElement.getAsJsonObject().has(id) && 322 | baseArrayElement.getAsJsonObject().get(id).isJsonPrimitive() && 323 | baseArrayElement.getAsJsonObject().get(id).getAsString().equals(value)){ 324 | //We have a match, now delete it 325 | final int index = i; 326 | removeJsonByType(baseArrayElement, removeArrayElement, () -> baseArray.remove(index)); 327 | break; 328 | } 329 | } 330 | }else{ 331 | System.err.println("Json Parser - Remove: Found CPWHERE tag, but 'id' or 'value' tags were missing"); 332 | } 333 | } 334 | } 335 | //If you don't give me a hint on what entry you mean, I'm afraid I can't do anything for you. 336 | } 337 | }else{ 338 | deleteAction.run(); 339 | } 340 | } 341 | 342 | public static void removeJSON(JsonObject base, JsonObject remove){ 343 | //iterate over all entries in remove 344 | for(String removeKey : remove.keySet()){ 345 | //If base does not have this tag, just ignore, else handle depending on type 346 | if(base.has(removeKey)){ 347 | JsonElement removeElement = remove.get(removeKey); 348 | JsonElement baseElement = base.get(removeKey); 349 | removeJsonByType(baseElement, removeElement, () -> base.remove(removeKey)); 350 | } 351 | } 352 | } 353 | 354 | public static boolean contains(Iterable l, Predicate test){ 355 | for(T t : l){ 356 | if(test.test(t)) 357 | return true; 358 | } 359 | return false; 360 | } 361 | 362 | public static T getFirst(Iterable l, Predicate test){ 363 | for(T t : l){ 364 | if(test.test(t)) 365 | return t; 366 | } 367 | return null; 368 | } 369 | 370 | public static byte[] concat(byte[] a, byte[] b){ 371 | byte[] c = new byte[a.length + b.length]; 372 | System.arraycopy(a, 0, c, 0, a.length); 373 | System.arraycopy(b, 0, c, a.length, b.length); 374 | return c; 375 | } 376 | 377 | public static byte[] encodeInt(int num){ 378 | return new byte[] { 379 | (byte)(num), 380 | (byte)(num >>> 8), 381 | (byte)(num >>> 16), 382 | (byte)(num >>> 24)}; 383 | } 384 | 385 | public static int decodeInt(byte[] num){ 386 | if(num.length != 4) 387 | throw new NumberFormatException("Array size must be 4"); 388 | return ((num[0]) & 0xFF) + ((num[1] & 0xFF) << 8) + 389 | ((num[2] & 0xFF) << 16) + ((num[3] & 0xFF) << 24); 390 | } 391 | 392 | public static void writeInt(ByteArrayOutputStream out, int i) throws IOException{ 393 | out.write(encodeInt(i)); 394 | } 395 | 396 | public static void writeString(ByteArrayOutputStream out, String s, byte terminator) throws IOException{ 397 | out.write((byte) s.length()); 398 | out.write(s.getBytes()); 399 | out.write(terminator); 400 | } 401 | 402 | } 403 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/annotation/Async.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.annotation; 2 | 3 | import static java.lang.annotation.RetentionPolicy.CLASS; 4 | import static java.lang.annotation.ElementType.METHOD; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Annotated methods should be called asynchonously, because they may be blocking the thread for some time. 11 | * @author Lars 12 | * 13 | */ 14 | @Retention(CLASS) 15 | @Target(METHOD) 16 | public @interface Async { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/annotation/Calculated.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.annotation; 2 | 3 | import static java.lang.annotation.ElementType.METHOD; 4 | import static java.lang.annotation.RetentionPolicy.CLASS; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * The object returned by the annotated method is newly cfeated eery time the method is called 11 | * This method should only be used in cases where it isn't obvoius that the returned object is not 12 | * a field or stored in a variable. 13 | * @author Lars b. 14 | * @version 1.0 15 | */ 16 | @Retention(CLASS) 17 | @Target(METHOD) 18 | public @interface Calculated { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/annotation/Shortcut.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.annotation; 2 | 3 | import static java.lang.annotation.ElementType.METHOD; 4 | import static java.lang.annotation.RetentionPolicy.CLASS; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Annotated methods are shortcuts for other methods. The name of a method call 11 | * with the same result is stored in the value parameter of this annotation. 12 | * @author Lars b. 13 | * @version 1.0 14 | */ 15 | @Retention(CLASS) 16 | @Target(METHOD) 17 | public @interface Shortcut { 18 | String value(); 19 | } 20 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/annotation/Unmodifiable.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.annotation; 2 | 3 | import static java.lang.annotation.ElementType.METHOD; 4 | import static java.lang.annotation.RetentionPolicy.CLASS; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Annotated methods return unmodifiable/immutable objects. 11 | * @author Lars B. 12 | * @version 1.0 13 | */ 14 | @Retention(CLASS) 15 | @Target(METHOD) 16 | public @interface Unmodifiable { 17 | } 18 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/controls/CheckBoxList.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.controls; 2 | 3 | import java.awt.Component; 4 | import java.awt.event.MouseAdapter; 5 | import java.awt.event.MouseEvent; 6 | 7 | import javax.swing.DefaultListModel; 8 | import javax.swing.JCheckBox; 9 | import javax.swing.JList; 10 | import javax.swing.ListCellRenderer; 11 | import javax.swing.ListModel; 12 | import javax.swing.ListSelectionModel; 13 | import javax.swing.UIManager; 14 | import javax.swing.border.Border; 15 | import javax.swing.border.EmptyBorder; 16 | 17 | public class CheckBoxList extends JList{ 18 | private static final long serialVersionUID = 2796368191296898045L; 19 | 20 | protected static Border noFocusBorder = 21 | new EmptyBorder(1, 1, 1, 1); 22 | 23 | public CheckBoxList() 24 | { 25 | super(new DefaultListModel<>()); 26 | 27 | setCellRenderer(new CellRenderer()); 28 | 29 | addMouseListener(new MouseAdapter() 30 | { 31 | public void mousePressed(MouseEvent e) 32 | { 33 | int index = locationToIndex(e.getPoint()); 34 | 35 | if (index != -1) { 36 | JCheckBox checkbox = (JCheckBox) 37 | getModel().getElementAt(index); 38 | checkbox.setSelected( 39 | !checkbox.isSelected()); 40 | repaint(); 41 | } 42 | } 43 | } 44 | ); 45 | 46 | setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 47 | } 48 | 49 | public void addCheckbox(JCheckBox checkBox) { 50 | ListModel currentList = this.getModel(); 51 | JCheckBox[] newList = new JCheckBox[currentList.getSize() + 1]; 52 | for (int i = 0; i < currentList.getSize(); i++) { 53 | newList[i] = (JCheckBox) currentList.getElementAt(i); 54 | } 55 | newList[newList.length - 1] = checkBox; 56 | setListData(newList); 57 | } 58 | 59 | protected class CellRenderer implements ListCellRenderer 60 | { 61 | public Component getListCellRendererComponent( 62 | JList list, JCheckBox value, int index, 63 | boolean isSelected, boolean cellHasFocus) 64 | { 65 | JCheckBox checkbox = (JCheckBox) value; 66 | checkbox.setBackground(isSelected ? 67 | getSelectionBackground() : getBackground()); 68 | checkbox.setForeground(isSelected ? 69 | getSelectionForeground() : getForeground()); 70 | checkbox.setEnabled(isEnabled()); 71 | checkbox.setFont(getFont()); 72 | checkbox.setFocusPainted(false); 73 | checkbox.setBorderPainted(true); 74 | checkbox.setBorder(isSelected ? 75 | UIManager.getBorder( 76 | "List.focusCellHighlightBorder") : noFocusBorder); 77 | return checkbox; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/controls/JAudioPlayer.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.controls; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.FlowLayout; 5 | import java.awt.event.MouseEvent; 6 | import java.awt.event.MouseListener; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import javax.sound.sampled.Clip; 10 | import javax.sound.sampled.LineEvent; 11 | import javax.sound.sampled.LineListener; 12 | import javax.swing.JButton; 13 | import javax.swing.JLabel; 14 | import javax.swing.JPanel; 15 | import javax.swing.JSlider; 16 | import javax.swing.Timer; 17 | 18 | import dev.lb.sound.ogg.JOrbisDecoder; 19 | 20 | public class JAudioPlayer extends JPanel implements MouseListener{ 21 | private static final long serialVersionUID = -7802517197319750907L; 22 | 23 | private Clip sound; 24 | 25 | private JButton playpause; 26 | private JButton stop; 27 | private JSlider progress; 28 | 29 | private JPanel contentHolder; 30 | private JLabel done; 31 | private JLabel total; 32 | private Timer barUpdater; 33 | 34 | private boolean playing; 35 | private boolean pressing; 36 | 37 | public JAudioPlayer(){ 38 | super(); 39 | contentHolder = new JPanel(); 40 | contentHolder.setLayout(new FlowLayout()); 41 | 42 | playpause = new JButton("Play"); 43 | playpause.addActionListener((e) -> { 44 | if(playing){ 45 | pause(); 46 | }else{ 47 | play(); 48 | } 49 | }); 50 | playpause.setPreferredSize(new Dimension(100, playpause.getPreferredSize().height)); 51 | stop = new JButton("Stop"); 52 | stop.addActionListener((e) -> { 53 | stop(true); 54 | }); 55 | stop.setPreferredSize(new Dimension(100, stop.getPreferredSize().height)); 56 | 57 | progress = new JSlider(); 58 | progress.setPreferredSize(new Dimension(200,progress.getPreferredSize().height)); 59 | progress.addMouseListener(this); 60 | progress.setSnapToTicks(false); 61 | 62 | barUpdater = new Timer(50, (e) -> updateSlider()); 63 | 64 | done = new JLabel("00:00"); 65 | total = new JLabel("00:00"); 66 | 67 | pressing = false; 68 | 69 | 70 | contentHolder.add(playpause); 71 | contentHolder.add(done); 72 | contentHolder.add(progress); 73 | contentHolder.add(total); 74 | contentHolder.add(stop); 75 | this.add(contentHolder); 76 | } 77 | 78 | public JAudioPlayer(Clip clip){ 79 | this(); 80 | this.setClip(clip); 81 | } 82 | 83 | public void play(){ 84 | playpause.setText("Pause"); 85 | if(sound == null || playing) 86 | return; 87 | playing = true; 88 | barUpdater.start(); 89 | new Thread(sound::start).start(); 90 | updateSlider(); 91 | } 92 | 93 | public void pause(){ 94 | if(sound == null || !playing) 95 | return; 96 | barUpdater.stop(); 97 | playpause.setText("Play"); 98 | playing = false; 99 | sound.stop(); 100 | updateSlider(); 101 | } 102 | 103 | public void stop(boolean force){ 104 | playpause.setText("Play"); 105 | if(sound == null || (!playing && !force)) 106 | return; 107 | barUpdater.stop(); 108 | sound.stop(); 109 | sound.flush(); 110 | sound.setFramePosition(0); 111 | playing = false; 112 | updateSlider(); 113 | } 114 | 115 | 116 | public void setClip(Clip c){ 117 | reset(); 118 | sound = c; 119 | sound.addLineListener(new LineListener(){ 120 | public void update(LineEvent e){ 121 | if(e.getType() == LineEvent.Type.STOP){ 122 | stop(false); 123 | } 124 | } 125 | }); 126 | progress.setMinimum(0); 127 | progress.setMaximum(sound.getFrameLength()); 128 | int sec = JOrbisDecoder.getLengthInSeconds(sound); 129 | total.setText(String.format("%02d:%02d", 130 | TimeUnit.SECONDS.toMinutes(sec), 131 | TimeUnit.SECONDS.toSeconds(sec) - 132 | TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(sec)))); 133 | } 134 | 135 | public void updateSlider(){ 136 | if(sound == null) 137 | return; 138 | if(!pressing) 139 | progress.setValue(sound.getFramePosition()); 140 | int sec = JOrbisDecoder.getPlayTimeInSeconds(sound); 141 | done.setText(String.format("%02d:%02d", 142 | TimeUnit.SECONDS.toMinutes(sec), 143 | TimeUnit.SECONDS.toSeconds(sec) - 144 | TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(sec)))); 145 | done.repaint(); 146 | } 147 | 148 | public void reset(){ 149 | if(sound == null) 150 | return; 151 | barUpdater.stop(); 152 | sound.stop(); 153 | sound.flush(); 154 | sound = null; 155 | playing = false; 156 | progress.setValue(0); 157 | done.setText("00:00"); 158 | total.setText("00:00"); 159 | } 160 | 161 | @Override 162 | public void mouseClicked(MouseEvent e) {} 163 | @Override 164 | public void mouseEntered(MouseEvent e) {} 165 | @Override 166 | public void mouseExited(MouseEvent e) {} 167 | @Override 168 | public void mousePressed(MouseEvent e) { 169 | pressing = true; 170 | } 171 | 172 | 173 | @Override 174 | public void mouseReleased(MouseEvent e) { 175 | if(sound != null && progress.getValue() != sound.getFramePosition()) 176 | sound.setFramePosition(progress.getValue()); 177 | pressing = false; 178 | } 179 | 180 | @Override 181 | public void setPreferredSize(Dimension preferredSize) {} 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/controls/JDraggableScrollPane.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.controls; 2 | 3 | import java.awt.Component; 4 | import java.awt.Point; 5 | import java.awt.Rectangle; 6 | import java.awt.event.MouseEvent; 7 | import java.awt.event.MouseListener; 8 | import java.awt.event.MouseMotionListener; 9 | 10 | import javax.swing.JComponent; 11 | import javax.swing.JScrollPane; 12 | import javax.swing.JViewport; 13 | 14 | public class JDraggableScrollPane extends JScrollPane implements MouseMotionListener, MouseListener{ 15 | private static final long serialVersionUID = -3429376543623295066L; 16 | 17 | private Point currentDrag; 18 | 19 | public JDraggableScrollPane(Component view, int vsbPolicy, int hsbPolicy) { 20 | super(view, vsbPolicy, hsbPolicy); 21 | this.getViewport().getView().addMouseMotionListener(this); 22 | this.getViewport().getView().addMouseListener(this); 23 | } 24 | 25 | public JDraggableScrollPane(Component view) { 26 | super(view); 27 | if(this.getViewport().getView() != null){ 28 | this.getViewport().getView().addMouseMotionListener(this); 29 | this.getViewport().getView().addMouseListener(this); 30 | } 31 | } 32 | 33 | @Override 34 | public void mouseDragged(MouseEvent e) { 35 | if (currentDrag != null) { 36 | JViewport viewPort = getViewport(); 37 | if (viewPort != null) { 38 | int deltaX = currentDrag.x - e.getX(); 39 | int deltaY = currentDrag.y - e.getY(); 40 | 41 | Rectangle view = viewPort.getViewRect(); 42 | view.x += deltaX; 43 | view.y += deltaY; 44 | 45 | ((JComponent) getViewport().getView()).scrollRectToVisible(view); 46 | } 47 | } 48 | } 49 | 50 | @Override 51 | public void mousePressed(MouseEvent e) { 52 | currentDrag = new Point(e.getPoint()); 53 | } 54 | 55 | @Override 56 | public void mouseMoved(MouseEvent e) {} 57 | @Override 58 | public void mouseEntered(MouseEvent e) {} 59 | @Override 60 | public void mouseExited(MouseEvent e) {} 61 | @Override 62 | public void mouseClicked(MouseEvent e) {} 63 | @Override 64 | public void mouseReleased(MouseEvent e) {} 65 | } 66 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/controls/JHistoryTextField.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.controls; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.awt.event.KeyListener; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import javax.swing.JTextField; 9 | 10 | public class JHistoryTextField extends JTextField implements KeyListener{ 11 | private static final long serialVersionUID = 4185648576613819237L; 12 | 13 | private List history; 14 | private int historyIndex; 15 | private String temp; 16 | 17 | public JHistoryTextField(){ 18 | super(); 19 | history = new ArrayList<>(); 20 | historyIndex = 0; 21 | addKeyListener(this); 22 | } 23 | 24 | public void clearHistory(){ 25 | history.clear(); 26 | } 27 | 28 | public void addHistory(){ 29 | history.add(getText()); 30 | temp = ""; 31 | historyIndex = history.size(); 32 | setText(""); 33 | } 34 | 35 | public void updateText(){ 36 | setText(history.get(historyIndex)); 37 | } 38 | 39 | @Override 40 | public void keyPressed(KeyEvent e) { 41 | if(e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_KP_UP){ 42 | historyIndex--; 43 | if(history.isEmpty()) return; 44 | if(historyIndex == history.size() - 1){ 45 | temp = getText(); 46 | } 47 | if(historyIndex < 0){ 48 | historyIndex = 0; 49 | } 50 | updateText(); 51 | }else if(e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_KP_DOWN){ 52 | historyIndex++; 53 | if(historyIndex == history.size()){ 54 | setText(temp); 55 | }else if(historyIndex > history.size()){ 56 | historyIndex = history.size(); 57 | }else{ 58 | updateText(); 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public void keyReleased(KeyEvent e) {} 65 | 66 | @Override 67 | public void keyTyped(KeyEvent e) {} 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/controls/JImageViewer.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.controls; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Graphics; 5 | import java.awt.Image; 6 | import java.awt.event.MouseWheelEvent; 7 | import java.awt.event.MouseWheelListener; 8 | import java.awt.image.BufferedImage; 9 | 10 | import javax.swing.JPanel; 11 | 12 | public class JImageViewer extends JPanel implements MouseWheelListener{ 13 | private static final long serialVersionUID = -4208234595443517732L; 14 | 15 | private Image image; 16 | private Image overlay; 17 | 18 | private double scrollFactor = 1.0D; 19 | 20 | public JImageViewer(Image mainImage) { 21 | image = mainImage; 22 | this.addMouseWheelListener(this); 23 | } 24 | 25 | @Override 26 | protected void paintComponent(Graphics g) { 27 | super.paintComponent(g); 28 | g.drawImage(image, 0, 0, (int) (getPreferredSize().width * scrollFactor), (int) (getPreferredSize().height * scrollFactor), getBackground(), this); 29 | if(overlay != null){ 30 | g.drawImage(overlay, 0, 0, (int) (getPreferredSize().width * scrollFactor), (int) (getPreferredSize().height * scrollFactor), getBackground(), this); 31 | } 32 | } 33 | 34 | protected Image getMainImage(){ 35 | return image; 36 | } 37 | 38 | public Image getOverlay() { 39 | return overlay; 40 | } 41 | 42 | public void setOverlay(Image overlay) { 43 | this.overlay = overlay; 44 | } 45 | 46 | 47 | @Override 48 | public Dimension getPreferredSize(){ 49 | return new Dimension((int) (image.getWidth(this) * scrollFactor), (int) (image.getHeight(this) * scrollFactor)); 50 | } 51 | 52 | @Override 53 | public Dimension getMinimumSize(){ 54 | return getPreferredSize(); 55 | } 56 | 57 | @Override 58 | public Dimension getMaximumSize(){ 59 | return getPreferredSize(); 60 | } 61 | 62 | public void setOverlay(BufferedImage overlay2) { 63 | overlay = overlay2; 64 | } 65 | 66 | @Override 67 | public void mouseWheelMoved(MouseWheelEvent e) { 68 | int clicks = e.getWheelRotation(); 69 | if(clicks < 0 && scrollFactor < 5){ //ZOOM IN 70 | scrollFactor *= 1.25D; 71 | }else if(clicks > 0 && scrollFactor > 0.2){ //ZOOM OUT 72 | scrollFactor /= 1.25D; 73 | } 74 | this.revalidate(); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/controls/JSpriteViewer.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.controls; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Graphics; 5 | import java.awt.event.MouseWheelEvent; 6 | import java.awt.image.BufferedImage; 7 | 8 | public class JSpriteViewer extends JImageViewer{ 9 | private static final long serialVersionUID = -5063070371711033414L; 10 | public static final BufferedImage EMPTY_SPRITE; 11 | static{ 12 | EMPTY_SPRITE = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); 13 | EMPTY_SPRITE.createGraphics().drawString("No sprite", 20, 50); 14 | } 15 | 16 | private BufferedImage currentSprite = EMPTY_SPRITE; 17 | private BufferedImage currentSpriteF = EMPTY_SPRITE; 18 | private boolean filter; 19 | private int cx, cy, cw, ch; 20 | 21 | public JSpriteViewer(BufferedImage mainImage, BufferedImage filterImage) { 22 | super(mainImage); 23 | this.setOverlay(filterImage); 24 | } 25 | 26 | @Override 27 | public Dimension getPreferredSize() { 28 | return new Dimension(0, 0); 29 | } 30 | 31 | public void setSprite(int x, int y, int width, int height){ 32 | currentSprite = ((BufferedImage) getMainImage()).getSubimage(x, y, width, height); 33 | currentSpriteF = getOverlay() == null ? null : ((BufferedImage) getOverlay()).getSubimage(x, y, width, height); 34 | cx = x; 35 | cy = y; 36 | cw = width; 37 | ch = height; 38 | this.repaint(); 39 | } 40 | 41 | public void setNoSprite(){ 42 | currentSprite = EMPTY_SPRITE; 43 | cx = 0; 44 | cy = 0; 45 | cw = 0; 46 | ch = 0; 47 | this.repaint(); 48 | } 49 | 50 | public void setUseFilter(boolean useFilter){ 51 | filter = useFilter; 52 | repaint(); 53 | } 54 | 55 | @Override 56 | public void mouseWheelMoved(MouseWheelEvent e) { 57 | //No scrolling allowed 58 | } 59 | 60 | @Override 61 | protected void paintComponent(Graphics g) { 62 | double ratio = ((double) currentSprite.getHeight(this) / (double) currentSprite.getWidth(this)); 63 | double thisRatio = ((double) getHeight() / (double) getWidth()); 64 | int scaleWidth, scaleHeight, startX, startY; 65 | if(ratio < thisRatio){//Wide 66 | scaleWidth = getWidth(); 67 | scaleHeight = (int) (getWidth() * ratio); 68 | startX = 0; 69 | startY = (getHeight() - scaleHeight) / 2; 70 | }else{//Tall 71 | scaleWidth = (int) (getHeight() * (1D / ratio)); 72 | scaleHeight = getHeight(); 73 | startX = (getWidth() - scaleWidth) / 2; 74 | startY = 0; 75 | } 76 | g.clearRect(0, 0, getWidth(), getHeight()); 77 | g.drawImage(filter ? currentSpriteF : currentSprite, startX, startY, scaleWidth, scaleHeight, this); 78 | } 79 | 80 | public int getCx() { 81 | return cx; 82 | } 83 | 84 | public int getCy() { 85 | return cy; 86 | } 87 | 88 | public int getCw() { 89 | return cw; 90 | } 91 | 92 | public int getCh() { 93 | return ch; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/dev/lb/cellpacker/controls/LaunchWindow.java: -------------------------------------------------------------------------------- 1 | package dev.lb.cellpacker.controls; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Dimension; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import javax.swing.DefaultListModel; 15 | import javax.swing.ImageIcon; 16 | import javax.swing.JButton; 17 | import javax.swing.JCheckBox; 18 | import javax.swing.JFrame; 19 | import javax.swing.JOptionPane; 20 | import javax.swing.JPanel; 21 | import javax.swing.JScrollPane; 22 | import javax.swing.ListModel; 23 | import javax.swing.border.EmptyBorder; 24 | 25 | import com.google.gson.Gson; 26 | import com.google.gson.JsonElement; 27 | import com.google.gson.JsonObject; 28 | import com.google.gson.JsonParser; 29 | 30 | import dev.lb.cellpacker.CellPackerMain; 31 | import dev.lb.cellpacker.Utils; 32 | import dev.lb.cellpacker.structure.ResourceCategory; 33 | import dev.lb.cellpacker.structure.ResourceFile; 34 | import dev.lb.cellpacker.structure.Script; 35 | import dev.lb.cellpacker.structure.resource.JsonResource; 36 | 37 | public class LaunchWindow extends JFrame{ 38 | private static final long serialVersionUID = 2286569184156822247L; 39 | 40 | public static final int NO_PROBLEM = 0; 41 | public static final int NEED_SETUP = 1; 42 | public static final int NOT_DEADCELLS = 2; 43 | 44 | private Map chk2script = new HashMap<>(); 45 | @SuppressWarnings("unused") 46 | private boolean update; 47 | private JButton launch; 48 | private JButton launchgl; 49 | private JButton reload; 50 | private JButton pack; 51 | private JButton help; 52 | private CheckBoxList chl; 53 | 54 | public LaunchWindow(boolean update){ 55 | super("CellPacker Launcher 2.1"); 56 | this.update = update; 57 | 58 | JPanel content = new JPanel(new BorderLayout()); 59 | JPanel buttons = new JPanel(); 60 | //buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS)); 61 | chl = new CheckBoxList(); 62 | content.add(new JScrollPane(chl), BorderLayout.CENTER); 63 | launch = new JButton("Start Dead Cells"); 64 | launchgl = new JButton("Start Dead Cells (OpenGL - Legacy)"); 65 | pack = new JButton("Start CellPacker"); 66 | help = new JButton("Help / About"); 67 | launch.setBorder(new EmptyBorder(5, 10, 5, 10)); 68 | launchgl.setBorder(new EmptyBorder(5, 10, 5, 10)); 69 | pack.setBorder(new EmptyBorder(5, 10, 5, 10)); 70 | help.setBorder(new EmptyBorder(5, 10, 5, 10)); 71 | launch.setPreferredSize(new Dimension(150, 50)); 72 | launchgl.setPreferredSize(new Dimension(150, 50)); 73 | reload = new JButton("Reload scripts"); 74 | reload.addActionListener((e) -> { 75 | chl.setModel(new DefaultListModel<>()); 76 | if(setupCorrect()){ 77 | reloadScripts(); 78 | }else{ 79 | chl.addCheckbox(new JCheckBox("")); 80 | } 81 | }); 82 | reload.setPreferredSize(new Dimension(150, 50)); 83 | pack.setPreferredSize(new Dimension(150, 50)); 84 | help.setPreferredSize(new Dimension(150, 50)); 85 | help.addActionListener((e) -> { 86 | Utils.showAboutDialog("For more information read the Readme on the github page:
https://github.com/ReBuilders101/CellPacker/blob/master/README.md", "About / Help", "https://github.com/ReBuilders101/CellPacker/blob/master/README.md"); 87 | }); 88 | pack.addActionListener((e) -> { 89 | CellPackerMain.mainFrame = new MainWindow(); 90 | CellPackerMain.mainFrame.setVisible(true); 91 | CellPackerMain.mainFrame.setIconImage(new ImageIcon(CellPackerMain.class.getResource("/resources/ico.png")).getImage()); 92 | this.dispose(); 93 | }); 94 | launch.addActionListener((e) -> { 95 | launch(false); 96 | }); 97 | launchgl.addActionListener((e) -> { 98 | launch(true); 99 | }); 100 | buttons.add(launch); 101 | buttons.add(launchgl); 102 | buttons.add(pack); 103 | buttons.add(reload); 104 | buttons.add(help); 105 | content.add(buttons, BorderLayout.SOUTH); 106 | 107 | if(setupCorrect()){ 108 | reloadScripts(); 109 | }else{ 110 | chl.addCheckbox(new JCheckBox("")); 111 | } 112 | 113 | 114 | this.add(content); 115 | this.setPreferredSize(new Dimension(800, 300)); 116 | this.setMinimumSize(new Dimension(400, 200)); 117 | this.pack(); 118 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 119 | } 120 | 121 | private void reloadScripts(){ 122 | //Get all available patches 123 | File patchDir = new File("./cpscripts"); 124 | for(File script : patchDir.listFiles((e) -> e.getPath().endsWith(".patch") && e.isFile())){ 125 | //read file to json 126 | byte[] data = new byte[(int) script.length()]; 127 | try(FileInputStream fis = new FileInputStream(script)){ 128 | fis.read(data); 129 | } catch (IOException e1) { 130 | e1.printStackTrace(); 131 | } 132 | String json = new String(data); 133 | if(Utils.isJsonValid(json)){ //Can be parsed 134 | JsonElement jsonElement = new JsonParser().parse(json); 135 | if(jsonElement.isJsonObject()){ 136 | String name = ""; 137 | String desc = ""; 138 | if(jsonElement.getAsJsonObject().has("info") && jsonElement.getAsJsonObject().get("info").isJsonObject()){ 139 | System.out.println("Found info section"); 140 | JsonObject info = jsonElement.getAsJsonObject().get("info").getAsJsonObject(); 141 | name = info.has("name") ? info.get("name").getAsString() : script.getName(); 142 | desc = info.has("desc") ? info.get("desc").getAsString() : "No description"; 143 | }else{ 144 | System.out.println("Found no info section"); 145 | //Add box by file name 146 | name = script.getName(); 147 | desc = "No description"; 148 | } 149 | JsonObject add = new JsonObject(); 150 | if(jsonElement.getAsJsonObject().has("add") && jsonElement.getAsJsonObject().get("add").isJsonObject()){ 151 | add = jsonElement.getAsJsonObject().get("add").getAsJsonObject(); 152 | } 153 | JsonObject remove = new JsonObject(); 154 | if(jsonElement.getAsJsonObject().has("remove") && jsonElement.getAsJsonObject().get("remove").isJsonObject()){ 155 | add = jsonElement.getAsJsonObject().get("remove").getAsJsonObject(); 156 | } 157 | Script sc = new Script(name, desc, add, remove); 158 | JCheckBox box = new JCheckBox(name); 159 | box.setToolTipText(desc); 160 | chk2script.put(box, sc); 161 | chl.addCheckbox(box); 162 | }else{ 163 | JOptionPane.showMessageDialog(this, "The file " + script.getName() + " is not a valid JSON Script and cannot be used as a script", "Error", JOptionPane.ERROR_MESSAGE); 164 | } 165 | }else{ 166 | JOptionPane.showMessageDialog(this, "The file " + script.getName() + " is not valid JSON and cannot be used as a script", "Error", JOptionPane.ERROR_MESSAGE); 167 | } 168 | 169 | //chl.addCheckbox(new JCheckBox(script.getName().substring(0, script.getName().lastIndexOf('.')))); 170 | } 171 | } 172 | 173 | private void launch(boolean gl){ 174 | //Patch resource 175 | ResourceFile rf = ResourceFile.fromFile(new File("./res.pak.cpbackup")); 176 | JsonResource datacdb = (JsonResource) rf.getRootContainer().getResource("data.cdb"); 177 | JsonObject cdbJson = new JsonParser().parse((String) datacdb.getContent()).getAsJsonObject(); 178 | ListModel model = chl.getModel(); 179 | List