diff --git a/src/display/bitmap.cpp b/src/display/bitmap.cpp index ac9bc431..c8d1a91b 100644 --- a/src/display/bitmap.cpp +++ b/src/display/bitmap.cpp @@ -105,6 +105,10 @@ return __VA_ARGS__; \ #define OUTLINE_SIZE 1 +#ifdef MKXPZ_RETRO +static uint64_t next_id = 1; +#endif // MKXPZ_RETRO + /* Normalize (= ensure width and * height are positive) */ static IntRect normalizedRect(const IntRect &rect) @@ -609,6 +613,9 @@ struct BitmapOpenHandler : FileSystem::OpenHandler }; Bitmap::Bitmap(Exception &exception, const char *filename) +#ifdef MKXPZ_RETRO + : id(next_id++) +#endif // MKXPZ_RETRO { std::string hiresPrefix = "Hires/"; std::string filenameStd = filename; @@ -792,6 +799,9 @@ Bitmap::Bitmap(Exception &exception, const char *filename) } Bitmap::Bitmap(Exception &exception, int width, int height, bool isHires) +#ifdef MKXPZ_RETRO + : id(next_id++) +#endif // MKXPZ_RETRO { if (width <= 0 || height <= 0) { exception = Exception(Exception::RGSSError, "failed to create bitmap"); @@ -835,6 +845,9 @@ Bitmap::Bitmap(Exception &exception, int width, int height, bool isHires) } Bitmap::Bitmap(Exception &exception, void *pixeldata, int width, int height) +#ifdef MKXPZ_RETRO + : id(next_id++) +#endif // MKXPZ_RETRO { #ifdef MKXPZ_RETRO SDL_Surface *surface = new SDL_Surface; @@ -900,6 +913,9 @@ Bitmap::Bitmap(Exception &exception, void *pixeldata, int width, int height) // frame is -2 for "any and all", -1 for "current", anything else for a specific frame Bitmap::Bitmap(Exception &exception, const Bitmap &other, int frame) +#ifdef MKXPZ_RETRO + : id(next_id++) +#endif // MKXPZ_RETRO { GUARD(other.guardDisposed(exception)); GUARD(other.ensureNonMega(exception)); @@ -964,6 +980,9 @@ Bitmap::Bitmap(Exception &exception, const Bitmap &other, int frame) } Bitmap::Bitmap(Exception &exception, TEXFBO &other) +#ifdef MKXPZ_RETRO + : id(next_id++) +#endif // MKXPZ_RETRO { Bitmap *hiresBitmap = nullptr; @@ -1006,6 +1025,9 @@ Bitmap::Bitmap(Exception &exception, TEXFBO &other) } Bitmap::Bitmap(Exception &exception, SDL_Surface *imgSurf, SDL_Surface *imgSurfHires, bool forceMega) +#ifdef MKXPZ_RETRO + : id(next_id++) +#endif // MKXPZ_RETRO { Bitmap *hiresBitmap = nullptr; @@ -3419,13 +3441,38 @@ bool Bitmap::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size) bool Bitmap::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size) { - if (!mkxp_sandbox::sandbox_deserialize(p->path, data, max_size)) return false; + { + std::string old_path = p->path; + if (!mkxp_sandbox::sandbox_deserialize(p->path, data, max_size)) return false; + if (p->path != old_path) { + deserModified = true; + } + } - if (!mkxp_sandbox::sandbox_deserialize((int32_t &)p->gl.width, data, max_size)) return false; - if (!mkxp_sandbox::sandbox_deserialize((int32_t &)p->gl.height, data, max_size)) return false; - if (!mkxp_sandbox::sandbox_deserialize(p->animation.enabled, data, max_size)) return false; + { + int32_t old_width = p->animation.enabled ? p->animation.width : p->gl.width; + if (!mkxp_sandbox::sandbox_deserialize((int32_t &)p->gl.width, data, max_size)) return false; + if (p->gl.width != old_width) { + deserModified = true; + } + } + { + int32_t old_height = p->animation.enabled ? p->animation.height : p->gl.height; + if (!mkxp_sandbox::sandbox_deserialize((int32_t &)p->gl.height, data, max_size)) return false; + if (p->gl.height != old_height) { + deserModified = true; + } + } + { + bool old_enabled = p->animation.enabled; + if (!mkxp_sandbox::sandbox_deserialize(p->animation.enabled, data, max_size)) return false; + if (p->animation.enabled != old_enabled) { + deserModified = true; + } + } if (p->animation.enabled) { + uint32_t old_frame = p->animation.currentFrameI(); p->animation.width = p->gl.width; p->animation.height = p->gl.height; if (!mkxp_sandbox::sandbox_deserialize(p->animation.fps, data, max_size)) return false; @@ -3435,6 +3482,9 @@ bool Bitmap::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &m if (!mkxp_sandbox::sandbox_deserialize((int32_t &)p->animation.lastFrame, data, max_size)) return false; if (!mkxp_sandbox::sandbox_deserialize(p->animation.playTime, data, max_size)) return false; if (!mkxp_sandbox::sandbox_deserialize(p->animation.startTime, data, max_size)) return false; + if (p->animation.currentFrameI() != old_frame) { + deserModified = true; + } } if (!mkxp_sandbox::sandbox_deserialize(p->font, data, max_size)) return false; diff --git a/src/display/bitmap.h b/src/display/bitmap.h index 1373a03b..4d8856e1 100644 --- a/src/display/bitmap.h +++ b/src/display/bitmap.h @@ -192,6 +192,7 @@ public: sigslot::signal<> modified; #ifdef MKXPZ_RETRO + const uint64_t id; // Globally unique nonzero ID for this bitmap, for change detection during save state deserialization bool deserModified; #endif // MKXPZ_RETRO diff --git a/src/display/gl/scene.h b/src/display/gl/scene.h index 1c8e0107..175dda60 100644 --- a/src/display/gl/scene.h +++ b/src/display/gl/scene.h @@ -49,6 +49,16 @@ public: /* Origin of contents */ Vec2i orig; + bool operator==(const Geometry &other) const + { + return rect == other.rect && orig == other.orig; + } + + bool operator!=(const Geometry &other) const + { + return !(*this == other); + } + Vec2i offset() const { return rect.pos() - orig; diff --git a/src/display/sprite.cpp b/src/display/sprite.cpp index 579c8e76..155e9483 100644 --- a/src/display/sprite.cpp +++ b/src/display/sprite.cpp @@ -965,7 +965,7 @@ void Sprite::sandbox_deserialize_end() if (isDisposed()) return; if (p->srcRect != nullptr) { p->srcRectCon = p->srcRect->valueChanged.connect(&SpritePrivate::onSrcRectChange, p); - if (!(*p->srcRect == p->deserSavedSrcRect)) { + if (*p->srcRect != p->deserSavedSrcRect) { p->onSrcRectChange(); } } diff --git a/src/display/tilemap.cpp b/src/display/tilemap.cpp index f79bf730..48c10475 100644 --- a/src/display/tilemap.cpp +++ b/src/display/tilemap.cpp @@ -329,9 +329,21 @@ struct TilemapPrivate /* Change watches */ sigslot::connection tilesetCon; +#ifdef MKXPZ_RETRO + uint64_t deserSavedTilesetId; +#endif // MKXPZ_RETRO sigslot::connection autotilesCon[autotileCount]; +#ifdef MKXPZ_RETRO + uint64_t deserSavedAutotileIds[autotileCount]; +#endif // MKXPZ_RETRO sigslot::connection mapDataCon; +#ifdef MKXPZ_RETRO + uint64_t deserSavedMapDataId; +#endif // MKXPZ_RETRO sigslot::connection prioritiesCon; +#ifdef MKXPZ_RETRO + uint64_t deserSavedPrioritiesId; +#endif // MKXPZ_RETRO /* Dispose watches */ sigslot::connection autotilesDispCon[autotileCount]; @@ -1488,7 +1500,6 @@ bool Tilemap::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size for (size_t i = 0; i < autotileCount; ++i) if (!mkxp_sandbox::sandbox_serialize((int32_t)p->atlas.nATFrames[i], data, max_size)) return false; - if (!mkxp_sandbox::sandbox_serialize(p->viewpPos, data, max_size)) return false; if (!mkxp_sandbox::sandbox_serialize(p->groundVert, data, max_size)) return false; for (size_t i = 0; i < zlayersMax; ++i) if (!mkxp_sandbox::sandbox_serialize(p->zlayerVert[i], data, max_size)) return false; @@ -1528,7 +1539,16 @@ bool Tilemap::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size bool Tilemap::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size) { if (!mkxp_sandbox::sandbox_deserialize(p->visible, data, max_size)) return false; - if (!mkxp_sandbox::sandbox_deserialize(p->origin, data, max_size)) return false; + { + Vec2i old_origin = p->origin; + if (!mkxp_sandbox::sandbox_deserialize(p->origin, data, max_size)) return false; + if (p->origin != old_origin) { + p->mapViewportDirty = true; + } + if (p->origin.y != old_origin.y) { + p->zOrderDirty = true; + } + } if (!mkxp_sandbox::sandbox_deserialize(p->dispPos, data, max_size)) return false; if (!mkxp_sandbox::sandbox_deserialize(p->atlas.size, data, max_size)) return false; @@ -1540,7 +1560,6 @@ bool Tilemap::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t & for (size_t i = 0; i < autotileCount; ++i) if (!mkxp_sandbox::sandbox_deserialize((int32_t &)p->atlas.nATFrames[i], data, max_size)) return false; - if (!mkxp_sandbox::sandbox_deserialize(p->viewpPos, data, max_size)) return false; if (!mkxp_sandbox::sandbox_deserialize(p->groundVert, data, max_size)) return false; for (size_t i = 0; i < zlayersMax; ++i) if (!mkxp_sandbox::sandbox_deserialize(p->zlayerVert[i], data, max_size)) return false; @@ -1566,7 +1585,13 @@ bool Tilemap::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t & if (!mkxp_sandbox::sandbox_deserialize(value, data, max_size)) return false; p->elem.activeLayers = value; } - if (!mkxp_sandbox::sandbox_deserialize(p->elem.sceneGeo, data, max_size)) return false; + { + Scene::Geometry old_geo = p->elem.sceneGeo; + if (!mkxp_sandbox::sandbox_deserialize(p->elem.sceneGeo, data, max_size)) return false; + if (p->elem.sceneGeo != old_geo) { + p->mapViewportDirty = true; + } + } if (!mkxp_sandbox::sandbox_deserialize(p->opacity, data, max_size)) return false; if (!mkxp_sandbox::sandbox_deserialize(p->blendType, data, max_size)) return false; @@ -1602,13 +1627,18 @@ void Tilemap::sandbox_deserialize_begin() p->tilesetDispCon.disconnect(); p->tilesetCon.disconnect(); + p->deserSavedTilesetId = p->tileset == nullptr ? 0 : p->tileset->id; - for (size_t i = 0; i < autotileCount; ++i) + for (size_t i = 0; i < autotileCount; ++i) { p->autotilesCon[i].disconnect(); + p->deserSavedAutotileIds[i] = p->autotiles[i] == nullptr ? 0 : p->autotiles[i]->id; + } p->mapDataCon.disconnect(); + p->deserSavedMapDataId = p->mapData == nullptr ? 0 : p->mapData->id; p->prioritiesCon.disconnect(); + p->deserSavedPrioritiesId = p->priorities == nullptr ? 0 : p->priorities->id; } void Tilemap::sandbox_deserialize_end() @@ -1651,7 +1681,7 @@ void Tilemap::sandbox_deserialize_end() if (isDisposed()) return; if (p->autotiles[i] != nullptr) { p->autotilesCon[i] = p->autotiles[i]->modified.connect(&TilemapPrivate::invalidateAtlasContents, p); - if (p->autotiles[i]->deserModified) { + if (p->autotiles[i]->deserModified || p->autotiles[i]->id != p->deserSavedAutotileIds[i]) { p->invalidateAtlasContents(); } } @@ -1660,7 +1690,7 @@ void Tilemap::sandbox_deserialize_end() if (isDisposed()) return; if (p->mapData != nullptr) { p->mapDataCon = p->mapData->modified.connect(&TilemapPrivate::invalidateBuffers, p); - if (p->mapData->deserModified) { + if (p->mapData->deserModified || p->mapData->id != p->deserSavedMapDataId) { p->invalidateBuffers(); } } @@ -1668,9 +1698,14 @@ void Tilemap::sandbox_deserialize_end() if (isDisposed()) return; if (p->priorities != nullptr) { p->prioritiesCon = p->priorities->modified.connect(&TilemapPrivate::invalidateBuffers, p); - if (p->priorities->deserModified) { + if (p->priorities->deserModified || p->priorities->id != p->deserSavedPrioritiesId) { p->invalidateBuffers(); } } + + if (isDisposed()) return; + if (p->tileset->deserModified) { + p->invalidateAtlasSize(); + } } #endif // MKXPZ_RETRO diff --git a/src/display/tilemapvx.cpp b/src/display/tilemapvx.cpp index ceba64d3..cc5e44b0 100644 --- a/src/display/tilemapvx.cpp +++ b/src/display/tilemapvx.cpp @@ -99,10 +99,19 @@ struct TilemapVXPrivate : public ViewportElement, TileAtlasVX::Reader bool mapViewportDirty; sigslot::connection mapDataCon; +#ifdef MKXPZ_RETRO + uint64_t deserSavedMapDataId; +#endif // MKXPZ_RETRO sigslot::connection flagsCon; +#ifdef MKXPZ_RETRO + uint64_t deserSavedFlagsId; +#endif // MKXPZ_RETRO sigslot::connection prepareCon; sigslot::connection bmChangedCons[BM_COUNT]; +#ifdef MKXPZ_RETRO + uint64_t deserSavedBitmapIds[BM_COUNT]; +#endif // MKXPZ_RETRO sigslot::connection bmDisposedCons[BM_COUNT]; struct AboveLayer : public ViewportElement @@ -635,6 +644,7 @@ bool TilemapVX::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_si { if (!mkxp_sandbox::sandbox_serialize(p->origin, data, max_size)) return false; if (!mkxp_sandbox::sandbox_serialize(p->dispPos, data, max_size)) return false; + if (!mkxp_sandbox::sandbox_serialize(p->sceneGeo, data, max_size)) return false; if (!mkxp_sandbox::sandbox_serialize(p->groundVert, data, max_size)) return false; if (!mkxp_sandbox::sandbox_serialize(p->aboveVert, data, max_size)) return false; if (!mkxp_sandbox::sandbox_serialize((mkxp_sandbox::wasm_size_t)p->allocQuads, data, max_size)) return false; @@ -657,8 +667,21 @@ bool TilemapVX::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_si bool TilemapVX::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size) { - if (!mkxp_sandbox::sandbox_deserialize(p->origin, data, max_size)) return false; + { + Vec2i old_origin = p->origin; + if (!mkxp_sandbox::sandbox_deserialize(p->origin, data, max_size)) return false; + if (p->origin != old_origin) { + p->mapViewportDirty = true; + } + } if (!mkxp_sandbox::sandbox_deserialize(p->dispPos, data, max_size)) return false; + { + Scene::Geometry old_geo = p->sceneGeo; + if (!mkxp_sandbox::sandbox_deserialize(p->sceneGeo, data, max_size)) return false; + if (p->sceneGeo != old_geo) { + p->mapViewportDirty = true; + } + } if (!mkxp_sandbox::sandbox_deserialize(p->groundVert, data, max_size)) return false; if (!mkxp_sandbox::sandbox_deserialize(p->aboveVert, data, max_size)) return false; { @@ -705,11 +728,14 @@ void TilemapVX::sandbox_deserialize_begin() for (size_t i = 0; i < BM_COUNT; ++i) { p->bmChangedCons[i].disconnect(); + p->deserSavedBitmapIds[i] = p->bitmaps[i] == nullptr ? 0 : p->bitmaps[i]->id; } p->mapDataCon.disconnect(); + p->deserSavedMapDataId = p->mapData == nullptr ? 0 : p->mapData->id; p->flagsCon.disconnect(); + p->deserSavedFlagsId = p->flags == nullptr ? 0 : p->flags->id; } void TilemapVX::sandbox_deserialize_end() @@ -734,7 +760,7 @@ void TilemapVX::sandbox_deserialize_end() if (isDisposed()) return; if (p->bitmaps[i] != nullptr) { p->bmChangedCons[i] = p->bitmaps[i]->modified.connect(&TilemapVXPrivate::invalidateAtlas, p); - if (p->bitmaps[i]->deserModified) { + if (p->bitmaps[i]->deserModified || p->bitmaps[i]->id != p->deserSavedBitmapIds[i]) { p->invalidateAtlas(); } } @@ -743,7 +769,7 @@ void TilemapVX::sandbox_deserialize_end() if (isDisposed()) return; if (p->mapData != nullptr) { p->mapDataCon = p->mapData->modified.connect(&TilemapVXPrivate::invalidateBuffers, p); - if (p->mapData->deserModified) { + if (p->mapData->deserModified || p->mapData->id != p->deserSavedMapDataId) { p->invalidateBuffers(); } } @@ -751,7 +777,7 @@ void TilemapVX::sandbox_deserialize_end() if (isDisposed()) return; if (p->flags != nullptr) { p->flagsCon = p->flags->modified.connect(&TilemapVXPrivate::invalidateBuffers, p); - if (p->flags->deserModified) { + if (p->flags->deserModified || p->flags->id != p->deserSavedFlagsId) { p->invalidateBuffers(); } } diff --git a/src/display/viewport.cpp b/src/display/viewport.cpp index 2de57bf5..1b2a7883 100644 --- a/src/display/viewport.cpp +++ b/src/display/viewport.cpp @@ -274,7 +274,7 @@ void Viewport::sandbox_deserialize_end() if (isDisposed()) return; if (p->rect != nullptr) { p->rectCon = p->rect->valueChanged.connect(&ViewportPrivate::onRectChange, p); - if (!(*p->rect == p->deserSavedRect)) { + if (*p->rect != p->deserSavedRect) { p->onRectChange(); } } diff --git a/src/display/window.cpp b/src/display/window.cpp index 1b0068c7..a29427e4 100644 --- a/src/display/window.cpp +++ b/src/display/window.cpp @@ -1053,7 +1053,7 @@ void Window::sandbox_deserialize_end() if (isDisposed()) return; if (p->cursorRect != nullptr) { p->cursorRectCon = p->cursorRect->valueChanged.connect(&WindowPrivate::markControlVertDirty, p); - if (!(*p->cursorRect == p->deserSavedCursorRect)) { + if (*p->cursorRect != p->deserSavedCursorRect) { p->markControlVertDirty(); } } diff --git a/src/display/windowvx.cpp b/src/display/windowvx.cpp index 69729e28..b38967bd 100644 --- a/src/display/windowvx.cpp +++ b/src/display/windowvx.cpp @@ -1284,7 +1284,7 @@ void WindowVX::sandbox_deserialize_end() if (isDisposed()) return; if (p->cursorRect != nullptr) { p->cursorRectCon = p->cursorRect->valueChanged.connect(&WindowVXPrivate::invalidateCursorVert, p); - if (!(*p->cursorRect == p->deserSavedCursorRect)) { + if (*p->cursorRect != p->deserSavedCursorRect) { p->invalidateCursorVert(); } } @@ -1292,7 +1292,7 @@ void WindowVX::sandbox_deserialize_end() if (isDisposed()) return; if (p->tone != nullptr) { p->toneCon = p->tone->valueChanged.connect(&WindowVXPrivate::invalidateBaseTex, p); - if (!(*p->tone == p->deserSavedTone)) { + if (*p->tone != p->deserSavedTone) { p->invalidateBaseTex(); } } diff --git a/src/etc/etc.cpp b/src/etc/etc.cpp index e2701f7e..3dc2c829 100644 --- a/src/etc/etc.cpp +++ b/src/etc/etc.cpp @@ -76,6 +76,11 @@ bool Color::operator==(const Color &o) const alpha == o.alpha; } +bool Color::operator!=(const Color &o) const +{ + return !(*this == o); +} + const Color &Color::operator=(const Color &o) { red = o.red; @@ -222,6 +227,11 @@ bool Tone::operator==(const Tone &o) const gray == o.gray; } +bool Tone::operator!=(const Tone &o) const +{ + return !(*this == o); +} + void Tone::set(double red, double green, double blue, double gray) { this->red = red; @@ -362,6 +372,11 @@ bool Rect::operator==(const Rect &o) const height == o.height; } +bool Rect::operator!=(const Rect &o) const +{ + return !(*this == o); +} + void Rect::operator=(const IntRect &rect) { x = rect.x; diff --git a/src/etc/etc.h b/src/etc/etc.h index 4e08c29a..5ad3d9ac 100644 --- a/src/etc/etc.h +++ b/src/etc/etc.h @@ -65,6 +65,7 @@ struct Color : public Serializable void set(double red, double green, double blue, double alpha); bool operator==(const Color &o) const; + bool operator!=(const Color &o) const; void setRed(double value); void setGreen(double value); @@ -119,6 +120,7 @@ struct Tone : public Serializable virtual ~Tone() {} bool operator==(const Tone &o) const; + bool operator!=(const Tone &o) const; void set(double red, double green, double blue, double gray); const Tone &operator=(const Tone &o); @@ -180,6 +182,7 @@ struct Rect : public Serializable Rect(const IntRect &r); bool operator==(const Rect &o) const; + bool operator!=(const Rect &o) const; void operator=(const IntRect &rect); void set(int x, int y, int w, int h); const Rect &operator=(const Rect &o); diff --git a/src/etc/table.cpp b/src/etc/table.cpp index e92292f4..88873b13 100644 --- a/src/etc/table.cpp +++ b/src/etc/table.cpp @@ -30,16 +30,26 @@ #ifdef MKXPZ_RETRO # include "sandbox-serial-util.h" + +static uint64_t next_id = 1; #endif // MKXPZ_RETRO /* Init normally */ Table::Table(int x, int y /*= 1*/, int z /*= 1*/) - : xs(x), ys(y), zs(z), + : +#ifdef MKXPZ_RETRO + id(next_id++), +#endif // MKXPZ_RETRO + xs(x), ys(y), zs(z), data(x*y*z) {} Table::Table(const Table &other) - : xs(other.xs), ys(other.ys), zs(other.zs), + : +#ifdef MKXPZ_RETRO + id(next_id++), +#endif // MKXPZ_RETRO + xs(other.xs), ys(other.ys), zs(other.zs), data(other.data) {} diff --git a/src/etc/table.h b/src/etc/table.h index ee82018c..41eb058d 100644 --- a/src/etc/table.h +++ b/src/etc/table.h @@ -69,6 +69,7 @@ public: sigslot::signal<> modified; #ifdef MKXPZ_RETRO + const uint64_t id; // Globally unique nonzero ID for this table, for change detection during save state deserialization bool deserModified; bool sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size) const;