Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a2863dd
Maniacs Patch - Save Image command
jetrotal Dec 4, 2025
e775637
Maniac SaveImage: async support
jetrotal Dec 4, 2025
892e35c
Maniacs Patch - Save Image command - Sprite Support, Proper Opaque an…
jetrotal Dec 7, 2025
a87d9f2
Maniacs Patch - GetGameInfo (pixel data extraction
jetrotal Dec 10, 2025
9538306
Maniacs Patch - SetPicturePixel Command
jetrotal Dec 10, 2025
d6ccc8f
BaseUi: Shorten CaptureScreen code
Ghabry Feb 11, 2026
e63832b
FileFinder: Correct implement open_generic_with_fallback based on fin…
Ghabry Feb 11, 2026
c92e28e
ManiacSaveImage: Simplify code
Ghabry Feb 11, 2026
a0f399f
WritePng: Support transparent PNG images
Ghabry Feb 11, 2026
c69f6a9
Cmd3026 already has a constant: WritePicture
Ghabry Feb 20, 2026
b037468
Maniacs Patch - GetPictureInfo (pixel data extraction)
jetrotal Dec 10, 2025
ddd4bb5
Maniac Get Game Info: Improve performance with fill and copy
Ghabry Feb 21, 2026
c413fc4
Move pixel extraction into a seperate function and implement pixel re…
Ghabry Feb 21, 2026
6ea133a
Canvas handling, move ManiacEditPicture so it matches the command order
Ghabry Feb 21, 2026
6234d34
EditPicture: Rework the code
Ghabry Feb 21, 2026
a98c609
Change default pixel format on little endian to ARGB.
Ghabry Feb 21, 2026
a730d8b
Implement saving of Canvas bitmaps
Ghabry Feb 21, 2026
9e7fe47
Add a helper function to create the path when writing to a file in th…
Ghabry Feb 23, 2026
54f7ce3
Invalidate the Picture cache when a picture is written to the disk
Ghabry Feb 23, 2026
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
12 changes: 10 additions & 2 deletions src/bitmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Bitmap::Bitmap(int width, int height, bool transparent) {
Bitmap::Bitmap(void *pixels, int width, int height, int pitch, const DynamicFormat& _format) {
format = _format;
pixman_format = find_format(format);
Init(width, height, pixels, pitch, false);
Init(width, height, pixels, pitch, pixels == nullptr);
}

Bitmap::Bitmap(Filesystem_Stream::InputStream stream, bool transparent, uint32_t flags) {
Expand Down 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
3 changes: 2 additions & 1 deletion src/bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ class Bitmap {
static BitmapRef Create(int width, int height, bool transparent = true, int bpp = 0);

/**
* Creates a surface wrapper around existing pixel data.
* Creates a surface wrapper around pixel data.
* When the pixel data is NULL the data is allocated and managed by the bitmap.
*
* @param pixels pointer to pixel data.
* @param width surface width.
Expand Down
11 changes: 11 additions & 0 deletions src/cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ namespace {
BitmapRef bmp;

const auto key = MakeHashKey(s.directory, filename, transparent, extra_flags);

auto it = cache.find(key);
if (it == cache.end()) {
if (filename == CACHE_DEFAULT_BITMAP) {
Expand Down Expand Up @@ -529,6 +530,16 @@ BitmapRef Cache::SpriteEffect(const BitmapRef& src_bitmap, const Rect& rect, boo
} else { return it->second.lock(); }
}

void Cache::Invalidate(std::string_view section) {
for (auto it = cache.begin(); it != cache.end(); ) {
if (StartsWith(it->first, section)) {
it = cache.erase(it);
} else {
++it;
}
}
}

void Cache::Clear() {
cache_effects.clear();
cache.clear();
Expand Down
6 changes: 6 additions & 0 deletions src/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ namespace Cache {
BitmapRef Tile(std::string_view filename, int tile_id);
BitmapRef SpriteEffect(const BitmapRef& src_bitmap, const Rect& rect, bool flip_x, bool flip_y, const Tone& tone, const Color& blend);

/**
* Removes all cached entries of the given section
*
* @param section Cache section to remove
*/
void Invalidate(std::string_view section);
void Clear();
void ClearAll();

Expand Down
56 changes: 31 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 All @@ -532,6 +519,25 @@ Filesystem_Stream::InputStream FileFinder::OpenText(std::string_view name) {
return open_generic_with_fallback("Text", name, args);
}

Filesystem_Stream::OutputStream FileFinder::OpenWrite(std::string_view name) {
std::string orig_name = FileFinder::MakeCanonical(name, 1);

std::string filename = FileFinder::Save().FindFile(name);

if (filename.empty()) {
// File not found: Create directory hierarchy to ensure file creation succeeds
auto dir = FileFinder::GetPathAndFilename(orig_name).first;

if (!dir.empty() && !FileFinder::Save().MakeDirectory(dir, false)) {
return Filesystem_Stream::OutputStream();
}

filename = orig_name;
}

return FileFinder::Save().OpenOutputStream(filename);
}

bool FileFinder::IsMajorUpdatedTree() {
auto fs = Game();
assert(fs);
Expand Down
11 changes: 11 additions & 0 deletions src/filefinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <cstdio>
#include <ios>
#include <istream>
#include <string_view>
#include <unordered_map>
#include <vector>

Expand Down Expand Up @@ -229,6 +230,16 @@ namespace FileFinder {
*/
Filesystem_Stream::InputStream OpenText(std::string_view name);

/**
* Opens a given file for writing in the save directory.
* Sanitizes the path and creates the directory hierarchy to the file when
* necessary.
*
* @param name
* @return Filesystem_Stream::OutputStream
*/
Filesystem_Stream::OutputStream OpenWrite(std::string_view name);

/**
* Appends name to directory.
*
Expand Down
Loading
Loading