|
16 | 16 | */ |
17 | 17 |
|
18 | 18 | #include <cmath> |
| 19 | +#include <zlib.h> |
19 | 20 | #include "bitmap.h" |
| 21 | +#include "lcf/rpg/savepicture.h" |
20 | 22 | #include "options.h" |
21 | 23 | #include "cache.h" |
22 | 24 | #include "output.h" |
@@ -102,7 +104,7 @@ std::vector<lcf::rpg::SavePicture> Game_Pictures::GetSaveData() const { |
102 | 104 | save.reserve(data_size); |
103 | 105 |
|
104 | 106 | for (auto& pic: pictures) { |
105 | | - save.push_back(pic.data); |
| 107 | + save.push_back(pic.OnSave()); |
106 | 108 | } |
107 | 109 |
|
108 | 110 | // RPG_RT Save game data always has a constant number of pictures |
@@ -367,6 +369,10 @@ void Game_Pictures::Picture::MakeRequestImportant() const { |
367 | 369 | void Game_Pictures::RequestPictureSprite(Picture& pic) { |
368 | 370 | const auto& name = pic.data.name; |
369 | 371 | if (name.empty()) { |
| 372 | + if (Player::IsPatchManiac()) { |
| 373 | + pic.LoadCanvas(); |
| 374 | + } |
| 375 | + |
370 | 376 | return; |
371 | 377 | } |
372 | 378 |
|
@@ -503,6 +509,120 @@ void Game_Pictures::Picture::AttachWindow(const Window_Base& window) { |
503 | 509 | ApplyOrigin(false); |
504 | 510 | } |
505 | 511 |
|
| 512 | +void Game_Pictures::Picture::LoadCanvas() { |
| 513 | + if (data.maniac_image_data.empty()) { |
| 514 | + return; |
| 515 | + } |
| 516 | + |
| 517 | + // Size is stored in the (unused in later 2k3) current_bot_trans, wtf? |
| 518 | + std::array<uint32_t, 2> dim; |
| 519 | + std::memcpy(dim.data(), &data.current_bot_trans, 8); |
| 520 | + |
| 521 | + // Image data is a compressed buffer (deflate) |
| 522 | + auto& compressed = data.maniac_image_data; |
| 523 | + z_stream strm{}; |
| 524 | + strm.next_in = const_cast<Bytef*>(compressed.data()); |
| 525 | + strm.avail_in = static_cast<uInt>(compressed.size()); |
| 526 | + |
| 527 | + if (inflateInit(&strm) != Z_OK) { |
| 528 | + Output::Warning("LoadCanvas (Pic {}}: inflateInit failed", data.ID); |
| 529 | + return; |
| 530 | + } |
| 531 | + |
| 532 | + std::vector<unsigned char> output; |
| 533 | + const size_t CHUNK_SIZE = 16384; |
| 534 | + unsigned char temp[CHUNK_SIZE]; |
| 535 | + |
| 536 | + int ret; |
| 537 | + do { |
| 538 | + strm.next_out = temp; |
| 539 | + strm.avail_out = CHUNK_SIZE; |
| 540 | + |
| 541 | + ret = inflate(&strm, Z_NO_FLUSH); |
| 542 | + |
| 543 | + if (ret != Z_OK && ret != Z_STREAM_END) { |
| 544 | + inflateEnd(&strm); |
| 545 | + Output::Warning("LoadCanvas (Pic {}}: inflate failed (err={})", data.ID, ret); |
| 546 | + return; |
| 547 | + } |
| 548 | + |
| 549 | + size_t bytes_produced = CHUNK_SIZE - strm.avail_out; |
| 550 | + output.insert(output.end(), temp, temp + bytes_produced); |
| 551 | + } while (ret != Z_STREAM_END); |
| 552 | + |
| 553 | + inflateEnd(&strm); |
| 554 | + |
| 555 | + if (output.size() != dim[0] * dim[1] * 4) { |
| 556 | + Output::Warning("LoadCanvas (Pic {}): Wrong buffer size", data.ID); |
| 557 | + return; |
| 558 | + } |
| 559 | + |
| 560 | + // Convert from Maniac Patch format to our screen format |
| 561 | + auto format = format_B8G8R8A8_a().format(); |
| 562 | + auto bmp = Bitmap::Create(output.data(), dim[0], dim[1], dim[0] * 4, format); |
| 563 | + CreateSprite(); |
| 564 | + sprite->SetBitmap(Bitmap::Create(*bmp, bmp->GetRect())); |
| 565 | + data.maniac_image_data = {}; // Save memory |
| 566 | + data.easyrpg_type = lcf::rpg::SavePicture::EasyRpgType_canvas; |
| 567 | +} |
| 568 | + |
| 569 | +lcf::rpg::SavePicture Game_Pictures::Picture::OnSave() const { |
| 570 | + auto save_data = data; |
| 571 | + |
| 572 | + if (IsCanvas()) { |
| 573 | + // Write compressed image data into the savefile |
| 574 | + auto& bitmap = *sprite->GetBitmap(); |
| 575 | + |
| 576 | + // Convert from our screen format to Maniac Patch format |
| 577 | + auto format = format_B8G8R8A8_a().format(); |
| 578 | + auto bmp_out = Bitmap::Create(nullptr, bitmap.width(), bitmap.height(), bitmap.width() * 4, format); |
| 579 | + bmp_out->Blit(0, 0, bitmap, bitmap.GetRect(), Opacity::Opaque()); |
| 580 | + |
| 581 | + // Compress |
| 582 | + z_stream strm{}; |
| 583 | + strm.next_in = reinterpret_cast<Bytef*>(bmp_out->pixels()); |
| 584 | + strm.avail_in = static_cast<uInt>(bitmap.pitch() * bitmap.height()); |
| 585 | + |
| 586 | + if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) { |
| 587 | + Output::Warning("LoadCanvas (Pic {}}: deflateInit failed", data.ID); |
| 588 | + return {}; |
| 589 | + } |
| 590 | + |
| 591 | + std::vector<unsigned char> output; |
| 592 | + const size_t CHUNK_SIZE = 16384; |
| 593 | + unsigned char temp[CHUNK_SIZE]; |
| 594 | + |
| 595 | + int ret; |
| 596 | + do { |
| 597 | + strm.next_out = temp; |
| 598 | + strm.avail_out = CHUNK_SIZE; |
| 599 | + |
| 600 | + ret = deflate(&strm, Z_FINISH); |
| 601 | + |
| 602 | + if (ret != Z_OK && ret != Z_STREAM_END) { |
| 603 | + deflateEnd(&strm); |
| 604 | + Output::Warning("LoadCanvas (Pic {}}: deflate failed", data.ID); |
| 605 | + return {}; |
| 606 | + } |
| 607 | + |
| 608 | + size_t bytes_produced = CHUNK_SIZE - strm.avail_out; |
| 609 | + output.insert(output.end(), temp, temp + bytes_produced); |
| 610 | + } while (ret != Z_STREAM_END); |
| 611 | + |
| 612 | + deflateEnd(&strm); |
| 613 | + |
| 614 | + // Save the data |
| 615 | + save_data.maniac_image_data = output; |
| 616 | + |
| 617 | + std::array<uint32_t, 2> dim; |
| 618 | + dim[0] = bitmap.width(); |
| 619 | + dim[1] = bitmap.height(); |
| 620 | + std::memcpy(&save_data.current_bot_trans, dim.data(), 8); |
| 621 | + } |
| 622 | + |
| 623 | + return save_data; |
| 624 | +} |
| 625 | + |
506 | 626 | bool Game_Pictures::Picture::IsNormalPicture() const { |
507 | 627 | return data.easyrpg_type == lcf::rpg::SavePicture::EasyRpgType_default; |
508 | 628 | } |
|
0 commit comments