Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/baseui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ BaseUi::BaseUi(const Game_Config& cfg)
}

BitmapRef BaseUi::CaptureScreen() {
BitmapRef capture = Bitmap::Create(main_surface->width(), main_surface->height(), false);
capture->BlitFast(0, 0, *main_surface, main_surface->GetRect(), Opacity::Opaque());
return capture;
return Bitmap::Create(*main_surface, main_surface->GetRect(), false);
}

void BaseUi::CleanDisplay() {
Expand Down
10 changes: 9 additions & 1 deletion src/bitmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,19 @@ bool Bitmap::WritePNG(std::ostream& os) const {
auto format = PIXMAN_b8g8r8;
#endif

if (GetTransparent()) {
#ifdef WORDS_BIGENDIAN
format = PIXMAN_r8g8b8a8;
#else
format = PIXMAN_a8b8g8r8;
#endif
}

auto dst = PixmanImagePtr{pixman_image_create_bits(format, width, height, &data.front(), stride)};
pixman_image_composite32(PIXMAN_OP_SRC, bitmap.get(), NULL, dst.get(),
0, 0, 0, 0, 0, 0, width, height);

return ImagePNG::Write(os, width, height, &data.front());
return ImagePNG::Write(os, width, height, &data.front(), GetTransparent());
}

size_t Bitmap::GetSize() const {
Expand Down
37 changes: 12 additions & 25 deletions src/filefinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,23 +432,6 @@ std::string find_generic(const DirectoryTree::Args& args) {
return FileFinder::Game().FindFile(args);
}

std::string find_generic_with_fallback(DirectoryTree::Args& args) {
// Searches first in the Save directory (because the game could have written
// files there, then in the Game directory.
// Disable this behaviour when Game and Save are shared as this breaks the
// translation redirection.
if (Player::shared_game_and_save_directory) {
return find_generic(args);
}

std::string found = FileFinder::Save().FindFile(args);
if (found.empty()) {
return find_generic(args);
}

return found;
}

std::string FileFinder::FindImage(std::string_view dir, std::string_view name) {
DirectoryTree::Args args = { MakePath(dir, name), IMG_TYPES, 1, false };
return find_generic(args);
Expand Down Expand Up @@ -490,16 +473,20 @@ Filesystem_Stream::InputStream open_generic(std::string_view dir, std::string_vi
}

Filesystem_Stream::InputStream open_generic_with_fallback(std::string_view dir, std::string_view name, DirectoryTree::Args& args) {
if (!Tr::GetCurrentTranslationId().empty()) {
auto tr_fs = Tr::GetCurrentTranslationFilesystem();
auto is = tr_fs.OpenFile(args);
if (is) {
return is;
}
// Searches first in the Save directory (because the game could have written
// files there, then in the Game directory.
// Disable this behaviour when Game and Save are shared as this breaks the
// translation redirection.
if (Player::shared_game_and_save_directory) {
return open_generic(dir, name, args);
}

auto is = FileFinder::Save().OpenFile(args);
if (!is) { is = open_generic(dir, name, args); }

if (!is) {
is = open_generic(dir, name, args);
}

if (!is) {
Output::Debug("Unable to open in either Game or Save: {}/{}", dir, name);
}
Expand All @@ -509,7 +496,7 @@ Filesystem_Stream::InputStream open_generic_with_fallback(std::string_view dir,

Filesystem_Stream::InputStream FileFinder::OpenImage(std::string_view dir, std::string_view name) {
DirectoryTree::Args args = { MakePath(dir, name), IMG_TYPES, 1, false };
return open_generic(dir, name, args);
return open_generic_with_fallback(dir, name, args);
}

Filesystem_Stream::InputStream FileFinder::OpenMusic(std::string_view name) {
Expand Down
121 changes: 121 additions & 0 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "async_handler.h"
#include "game_dynrpg.h"
#include "filefinder.h"
#include "cache.h"
#include "game_destiny.h"
#include "game_map.h"
#include "game_event.h"
Expand Down Expand Up @@ -806,6 +807,8 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) {
return CmdSetup<&Game_Interpreter::CommandManiacSetGameOption, 4>(com);
case Cmd::Maniac_ControlStrings:
return CmdSetup<&Game_Interpreter::CommandManiacControlStrings, 8>(com);
case static_cast<Cmd>(3026): //Maniac_SaveImage
return CmdSetup<&Game_Interpreter::CommandManiacSaveImage, 5>(com);
case Cmd::Maniac_CallCommand:
return CmdSetup<&Game_Interpreter::CommandManiacCallCommand, 6>(com);
case Cmd::Maniac_GetGameInfo:
Expand Down Expand Up @@ -5288,6 +5291,124 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const&
return true;
}

bool Game_Interpreter::CommandManiacSaveImage(lcf::rpg::EventCommand const& com) {
if (!Player::IsPatchManiac()) {
return true;
}

/*
TPC Structure Reference:
@img.save .screen .dst "filename"
@img.save .pic ID .static/.dynamic .opaq .dst "filename"

Parameters:
[0] Packing:
Bits 0-3: Picture ID Mode (0: Const, 1: Var, 2: Indirect)
Bits 4-7: Filename Mode (0: Literal, 1: String/Variable)
[1] Target Type: 0 = Screen, 1 = Picture
[2] Picture ID (Value)
[3] Filename ID (Value if not literal)
[4] Flags:
Bit 0: Dynamic (1) / Static (0)
Bit 1: Opaque (1)
*/

int target_type = com.parameters[1];

std::string filename = ToString(CommandStringOrVariableBitfield(com, 0, 1, 3));

if (filename.empty()) {
Output::Warning("ManiacSaveImage: Filename is empty");
return true;
}

// Decode Flags
int flags = com.parameters[4];
bool apply_effects = (flags & 1) != 0;
bool is_opaque = (flags & 2) != 0;

// Prepare Bitmap
BitmapRef bitmap;

if (target_type == 0) {
// Target: Screen (.screen)
bitmap = DisplayUi->CaptureScreen();
} else if (target_type == 1) {
// Target: Picture (.pic)
int pic_id = ValueOrVariableBitfield(com, 0, 0, 2);

if (pic_id <= 0) {
Output::Warning("ManiacSaveImage: Invalid Picture ID {}", pic_id);
return true;
}

auto& picture = Main_Data::game_pictures->GetPicture(pic_id);

if (picture.IsRequestPending()) {
picture.MakeRequestImportant();
_async_op = AsyncOp::MakeYieldRepeat();
return true;
}

const auto sprite = picture.sprite.get();

// Retrieve bitmap
if (picture.IsWindowAttached()) {
// Maniac ignores the opaque setting for String Picture
bitmap = picture.sprite->GetBitmap();
} else if (picture.data.name.empty()) {
// Not much we can do here (also shouldn't happen normally)
bitmap = picture.sprite->GetBitmap();
} else {
// Fetch picture with correct transparency
bitmap = Cache::Picture(picture.data.name, !is_opaque);
}

if (bitmap) {
// Determine Spritesheet frame
Rect src_rect = picture.sprite->GetSrcRect();

if (apply_effects) {
// .dynamic: Reflect color tone, flash, and other effects
auto tone = sprite->GetTone();
auto flash = sprite->GetFlashEffect();
auto flip_x = sprite->GetFlipX();
auto flip_y = sprite->GetFlipY();
bitmap = Cache::SpriteEffect(bitmap, src_rect, flip_x, flip_y, tone, flash);
} else if (src_rect != bitmap->GetRect()) {
// .static: Crop specific cell if it's a spritesheet
bitmap = Bitmap::Create(*bitmap, src_rect);
}
}
}
else {
Output::Warning("ManiacSaveImage: Unsupported target type {}", target_type);
return true;
}

// Save logic
if (bitmap) {
// Save to disk
// Ensure 'filename' has a valid extension (.png).
if (!EndsWith(Utils::LowerCase(filename), ".png")) {
filename += ".png";
}

auto found_file = FileFinder::Save().FindFile(filename);

auto os = FileFinder::Save().OpenOutputStream(found_file.empty() ? filename : found_file);
if (os) {
bitmap->WritePNG(os);
} else {
Output::Warning("ManiacSaveImage: Failed to open file for writing: {}", filename);
}
} else {
Output::Debug("ManiacSaveImage: Nothing to save (Target {})", target_type);
}

return true;
}

bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& com) {
if (!Player::IsPatchManiac()) {
return true;
Expand Down
1 change: 1 addition & 0 deletions src/game_interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext
bool CommandManiacChangePictureId(lcf::rpg::EventCommand const& com);
bool CommandManiacSetGameOption(lcf::rpg::EventCommand const& com);
bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com);
bool CommandManiacSaveImage(lcf::rpg::EventCommand const& com);
bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& com);
Expand Down
4 changes: 2 additions & 2 deletions src/image_png.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ static void flush_stream(png_structp out_ptr) {
reinterpret_cast<Filesystem_Stream::OutputStream*>(png_get_io_ptr(out_ptr))->flush();
}

bool ImagePNG::Write(std::ostream& os, uint32_t width, uint32_t height, uint32_t* data) {
bool ImagePNG::Write(std::ostream& os, uint32_t width, uint32_t height, uint32_t* data, bool transparent) {
png_structp write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!write) {
Output::Warning("Bitmap::WritePNG: error in png_create_write");
Expand Down Expand Up @@ -282,7 +282,7 @@ bool ImagePNG::Write(std::ostream& os, uint32_t width, uint32_t height, uint32_t
png_set_write_fn(write, &os, &write_data, &flush_stream);

png_set_IHDR(write, info, width, height, 8,
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
transparent ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(write, info);
png_write_image(write, ptrs);
Expand Down
2 changes: 1 addition & 1 deletion src/image_png.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
namespace ImagePNG {
bool Read(const void* buffer, bool transparent, ImageOut& output);
bool Read(Filesystem_Stream::InputStream& is, bool transparent, ImageOut& output);
bool Write(std::ostream& os, uint32_t width, uint32_t height, uint32_t* data);
bool Write(std::ostream& os, uint32_t width, uint32_t height, uint32_t* data, bool transparent);
}

#endif
6 changes: 6 additions & 0 deletions src/sprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class Sprite : public Drawable {
*/
void SetWaverPhase(double phase);

Color GetFlashEffect() const;

/**
* Set the flash effect color
*/
Expand Down Expand Up @@ -296,6 +298,10 @@ inline void Sprite::SetBushDepth(int bush_depth) {
bush_effect = bush_depth;
}

inline Color Sprite::GetFlashEffect() const {
return flash_effect;
}

inline void Sprite::SetFlashEffect(const Color &color) {
flash_effect = color;
}
Expand Down
Loading