Skip to content

Commit 55307fd

Browse files
committed
Implement saving of Canvas bitmaps
Most compatible with what Maniacs is doing At least we can load Maniacs images but Maniacs shows nothing
1 parent ccb6757 commit 55307fd

3 files changed

Lines changed: 124 additions & 2 deletions

File tree

src/game_interpreter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5404,6 +5404,7 @@ bool Game_Interpreter::CommandManiacEditPicture(lcf::rpg::EventCommand const& co
54045404
writable_bitmap = Bitmap::Create(*bitmap, src_rect, true);
54055405
}
54065406

5407+
picture.data.name = {};
54075408
picture.data.easyrpg_type = lcf::rpg::SavePicture::EasyRpgType_canvas;
54085409

54095410
// Packing: x pos, y pos, width, height, var_id

src/game_pictures.cpp

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
*/
1717

1818
#include <cmath>
19+
#include <zlib.h>
1920
#include "bitmap.h"
21+
#include "lcf/rpg/savepicture.h"
2022
#include "options.h"
2123
#include "cache.h"
2224
#include "output.h"
@@ -102,7 +104,7 @@ std::vector<lcf::rpg::SavePicture> Game_Pictures::GetSaveData() const {
102104
save.reserve(data_size);
103105

104106
for (auto& pic: pictures) {
105-
save.push_back(pic.data);
107+
save.push_back(pic.OnSave());
106108
}
107109

108110
// RPG_RT Save game data always has a constant number of pictures
@@ -367,6 +369,10 @@ void Game_Pictures::Picture::MakeRequestImportant() const {
367369
void Game_Pictures::RequestPictureSprite(Picture& pic) {
368370
const auto& name = pic.data.name;
369371
if (name.empty()) {
372+
if (Player::IsPatchManiac()) {
373+
pic.LoadCanvas();
374+
}
375+
370376
return;
371377
}
372378

@@ -503,6 +509,120 @@ void Game_Pictures::Picture::AttachWindow(const Window_Base& window) {
503509
ApplyOrigin(false);
504510
}
505511

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+
506626
bool Game_Pictures::Picture::IsNormalPicture() const {
507627
return data.easyrpg_type == lcf::rpg::SavePicture::EasyRpgType_default;
508628
}

src/game_pictures.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
// Headers
2222
#include <string>
23-
#include <deque>
2423
#include "async_handler.h"
2524
#include <lcf/rpg/savepicture.h>
2625
#include "sprite_picture.h"
@@ -127,6 +126,8 @@ class Game_Pictures {
127126
void OnMapScrolled(int dx, int dy);
128127

129128
void AttachWindow(const Window_Base& window);
129+
void LoadCanvas();
130+
lcf::rpg::SavePicture OnSave() const;
130131

131132
bool IsNormalPicture() const;
132133
bool IsWindowAttached() const;

0 commit comments

Comments
 (0)