Skip to content
Open
Show file tree
Hide file tree
Changes from all 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