mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-17 08:43:11 +00:00
Add UI and logic to recover a backup session
Changes: * Add DataRecoveryView * Split SubObjectsIO into an interface and SubObjectsFromSprite * Add a "setId" parameter to some doc::read_* functions (we need this functionality from session recovery)
This commit is contained in:
parent
5cf0396d98
commit
a4478e9306
@ -2,6 +2,12 @@
|
||||
<!-- Copyright (C) 2001-2015 by David Capello -->
|
||||
<gui>
|
||||
<vbox noborders="true" id="home_view" border="4" childspacing="2" expansive="true">
|
||||
<hbox noborders="true" id="recover_sprites_placeholder">
|
||||
<boxfiller />
|
||||
<button id="recover_sprites" text="Recover Lost Sprites" border="8" />
|
||||
<boxfiller />
|
||||
</hbox>
|
||||
|
||||
<hbox noborders="true" id="header_placeholder">
|
||||
<image file="icons/ase48.png" align="center" />
|
||||
<vbox border="4" childspacing="4">
|
||||
|
@ -227,8 +227,9 @@ add_library(app-lib
|
||||
context_flags.cpp
|
||||
crash/backup_observer.cpp
|
||||
crash/data_recovery.cpp
|
||||
crash/document_io.cpp
|
||||
crash/read_document.cpp
|
||||
crash/session.cpp
|
||||
crash/write_document.cpp
|
||||
document.cpp
|
||||
document_api.cpp
|
||||
document_exporter.cpp
|
||||
@ -294,6 +295,7 @@ add_library(app-lib
|
||||
ui/color_sliders.cpp
|
||||
ui/configure_timeline_popup.cpp
|
||||
ui/context_bar.cpp
|
||||
ui/data_recovery_view.cpp
|
||||
ui/devconsole_view.cpp
|
||||
ui/document_view.cpp
|
||||
ui/drop_down_button.cpp
|
||||
|
@ -194,6 +194,10 @@ void App::initialize(const AppOptions& options)
|
||||
// Default window title bar.
|
||||
updateDisplayTitleBar();
|
||||
|
||||
// Recover data
|
||||
if (!m_modules->m_recovery.sessions().empty())
|
||||
m_mainWindow->showDataRecovery(&m_modules->m_recovery);
|
||||
|
||||
m_mainWindow->openWindow();
|
||||
|
||||
// Redraw the whole screen.
|
||||
|
@ -63,7 +63,7 @@ void AddCel::onRedo()
|
||||
{
|
||||
Layer* layer = this->layer();
|
||||
|
||||
SubObjectsIO io(layer->sprite());
|
||||
SubObjectsFromSprite io(layer->sprite());
|
||||
bool has_data = (read8(m_stream) != 0);
|
||||
if (has_data) {
|
||||
ImageRef image(read_image(m_stream));
|
||||
|
@ -51,7 +51,7 @@ void AddLayer::onUndo()
|
||||
void AddLayer::onRedo()
|
||||
{
|
||||
Layer* folder = m_folder.layer();
|
||||
SubObjectsIO io(folder->sprite());
|
||||
SubObjectsFromSprite io(folder->sprite());
|
||||
Layer* newLayer = read_layer(m_stream, &io);
|
||||
Layer* afterThis = m_afterThis.layer();
|
||||
|
||||
|
@ -54,8 +54,11 @@ void BackupObserver::onAddDocument(doc::Document* document)
|
||||
void BackupObserver::onRemoveDocument(doc::Document* document)
|
||||
{
|
||||
TRACE("DataRecovery:: Remove document %p\n", document);
|
||||
base::scoped_lock hold(m_mutex);
|
||||
base::remove_from_container(m_documents, document);
|
||||
{
|
||||
base::scoped_lock hold(m_mutex);
|
||||
base::remove_from_container(m_documents, document);
|
||||
}
|
||||
m_session->removeDocument(static_cast<app::Document*>(document));
|
||||
}
|
||||
|
||||
void BackupObserver::backgroundThread()
|
||||
|
@ -86,6 +86,9 @@ DataRecovery::~DataRecovery()
|
||||
m_backup->stop();
|
||||
delete m_backup;
|
||||
|
||||
if (m_inProgress)
|
||||
m_inProgress->removeFromDisk();
|
||||
|
||||
m_inProgress.reset();
|
||||
}
|
||||
|
||||
|
265
src/app/crash/read_document.cpp
Normal file
265
src/app/crash/read_document.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/crash/read_document.h"
|
||||
|
||||
#include "app/console.h"
|
||||
#include "app/document.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/exception.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/serialization.h"
|
||||
#include "base/string.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/cel_data_io.h"
|
||||
#include "doc/cel_io.h"
|
||||
#include "doc/cels_range.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/frame_tag.h"
|
||||
#include "doc/frame_tag_io.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/string_io.h"
|
||||
#include "doc/subobjects_io.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
namespace app {
|
||||
namespace crash {
|
||||
|
||||
using namespace base::serialization;
|
||||
using namespace base::serialization::little_endian;
|
||||
using namespace doc;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define IFSTREAM(dir, name, fn) \
|
||||
std::ifstream name(base::from_utf8(base::join_path(dir, fn)), std::ifstream::binary);
|
||||
#else
|
||||
#define IFSTREAM(dir, name, fn) \
|
||||
std::ifstream name(base::join_path(dir, fn), std::ifstream::binary);
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
class Reader : public SubObjectsIO {
|
||||
public:
|
||||
Reader(const std::string& dir)
|
||||
: m_sprite(nullptr)
|
||||
, m_dir(dir) {
|
||||
}
|
||||
|
||||
Sprite* loadSprite(ObjectId sprId) {
|
||||
return loadObject<Sprite*>("spr", sprId, &Reader::readSprite);
|
||||
}
|
||||
|
||||
ImageRef getImageRef(ObjectId imageId) {
|
||||
if (m_images.find(imageId) != m_images.end())
|
||||
return m_images[imageId];
|
||||
|
||||
ImageRef image(loadObject<Image*>("img", imageId, &Reader::readImage));
|
||||
return m_images[imageId] = image;
|
||||
}
|
||||
|
||||
CelDataRef getCelDataRef(ObjectId celdataId) {
|
||||
if (m_celdatas.find(celdataId) != m_celdatas.end())
|
||||
return m_celdatas[celdataId];
|
||||
|
||||
CelDataRef celData(loadObject<CelData*>("celdata", celdataId, &Reader::readCelData));
|
||||
return m_celdatas[celdataId] = celData;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
T loadObject(const char* prefix, ObjectId id, T (Reader::*readMember)(std::ifstream&)) {
|
||||
TRACE(" - Restoring %s%d\n", prefix, id);
|
||||
|
||||
IFSTREAM(m_dir, s, prefix + base::convert_to<std::string>(id));
|
||||
|
||||
T obj = nullptr;
|
||||
if (s.good())
|
||||
obj = (this->*readMember)(s);
|
||||
|
||||
if (obj) {
|
||||
TRACE(" - %s%d restored successfully\n", prefix, id);
|
||||
return obj;
|
||||
}
|
||||
else {
|
||||
TRACE(" - %s%d was not restored\n", prefix, id);
|
||||
throw base::Exception(std::string("Error loading object ")
|
||||
+ prefix + base::convert_to<std::string>(id));
|
||||
}
|
||||
}
|
||||
|
||||
Sprite* readSprite(std::ifstream& s) {
|
||||
PixelFormat format = (PixelFormat)read8(s);
|
||||
int w = read16(s);
|
||||
int h = read16(s);
|
||||
color_t transparentColor = read32(s);
|
||||
|
||||
if (format != IMAGE_RGB &&
|
||||
format != IMAGE_INDEXED &&
|
||||
format != IMAGE_GRAYSCALE) {
|
||||
Console().printf("Invalid sprite format #%d\n", (int)format);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (w < 1 || h < 1 || w > 0xfffff || h > 0xfffff) {
|
||||
Console().printf("Invalid sprite dimension %dx%d\n", w, h);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
base::UniquePtr<Sprite> spr(new Sprite(format, w, h, 256));
|
||||
m_sprite = spr.get();
|
||||
spr->setTransparentColor(transparentColor);
|
||||
|
||||
// Frame durations
|
||||
frame_t nframes = read32(s);
|
||||
if (nframes >= 1) {
|
||||
spr->setTotalFrames(nframes);
|
||||
for (frame_t fr=0; fr<nframes; ++fr) {
|
||||
int msecs = read32(s);
|
||||
spr->setFrameDuration(fr, msecs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Console().printf("Invalid number of frames #%d\n", nframes);
|
||||
}
|
||||
|
||||
// Read layers
|
||||
int nlayers = read32(s);
|
||||
if (nlayers >= 1 && nlayers < 0xfffff) {
|
||||
for (int i = 0; i < nlayers; ++i) {
|
||||
ObjectId layId = read32(s);
|
||||
Layer* lay = loadObject<Layer*>("lay", layId, &Reader::readLayer);
|
||||
if (lay)
|
||||
spr->folder()->addLayer(lay);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Console().printf("Invalid number of layers #%d\n", nlayers);
|
||||
}
|
||||
|
||||
// Read palettes
|
||||
int npalettes = read32(s);
|
||||
if (npalettes >= 1 && npalettes < 0xfffff) {
|
||||
for (int i = 0; i < npalettes; ++i) {
|
||||
ObjectId palId = read32(s);
|
||||
Palette* pal = loadObject<Palette*>("pal", palId, &Reader::readPalette);
|
||||
if (pal)
|
||||
spr->setPalette(pal, true);
|
||||
}
|
||||
}
|
||||
|
||||
return spr.release();
|
||||
}
|
||||
|
||||
Layer* readLayer(std::ifstream& s) {
|
||||
LayerFlags flags = (LayerFlags)read32(s);
|
||||
ObjectType type = (ObjectType)read16(s);
|
||||
ASSERT(type == ObjectType::LayerImage);
|
||||
|
||||
std::string name = read_string(s);
|
||||
|
||||
if (type == ObjectType::LayerImage) {
|
||||
base::UniquePtr<LayerImage> lay(new LayerImage(m_sprite));
|
||||
lay->setName(name);
|
||||
lay->setFlags(flags);
|
||||
|
||||
// Cels
|
||||
int ncels = read32(s);
|
||||
for (int i=0; i<ncels; ++i) {
|
||||
ObjectId celId = read32(s);
|
||||
Cel* cel = loadObject<Cel*>("cel", celId, &Reader::readCel);
|
||||
if (cel)
|
||||
lay->addCel(cel);
|
||||
}
|
||||
return lay.release();
|
||||
}
|
||||
else {
|
||||
Console().printf("Unable to load layer named '%s', type #%d\n",
|
||||
name, (int)type);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Cel* readCel(std::ifstream& s) {
|
||||
return read_cel(s, this, false);
|
||||
}
|
||||
|
||||
CelData* readCelData(std::ifstream& s) {
|
||||
return read_celdata(s, this, false);
|
||||
}
|
||||
|
||||
Image* readImage(std::ifstream& s) {
|
||||
return read_image(s, false);
|
||||
}
|
||||
|
||||
Palette* readPalette(std::ifstream& s) {
|
||||
return read_palette(s);
|
||||
}
|
||||
|
||||
Sprite* m_sprite; // Used to pass the sprite in LayerImage() ctor
|
||||
std::string m_dir;
|
||||
std::map<ObjectId, ImageRef> m_images;
|
||||
std::map<ObjectId, CelDataRef> m_celdatas;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Public API
|
||||
|
||||
bool read_document_info(const std::string& dir, DocumentInfo& info)
|
||||
{
|
||||
ObjectId sprId;
|
||||
|
||||
if (base::is_file(base::join_path(dir, "doc"))) {
|
||||
IFSTREAM(dir, s, "doc");
|
||||
sprId = read32(s);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
IFSTREAM(dir, s, "spr" + base::convert_to<std::string>(sprId));
|
||||
info.format = (PixelFormat)read8(s);
|
||||
info.width = read16(s);
|
||||
info.height = read16(s);
|
||||
read32(s); // Ignore transparent color
|
||||
info.frames = read32(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
app::Document* read_document(const std::string& dir)
|
||||
{
|
||||
if (!base::is_file(base::join_path(dir, "doc")))
|
||||
return nullptr;
|
||||
|
||||
IFSTREAM(dir, s, "doc");
|
||||
ObjectId sprId = read32(s);
|
||||
|
||||
Reader reader(dir);
|
||||
Sprite* spr = reader.loadSprite(sprId);
|
||||
if (spr)
|
||||
return new app::Document(spr);
|
||||
else {
|
||||
Console().printf("Unable to load sprite #%d\n", sprId);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crash
|
||||
} // namespace app
|
34
src/app/crash/read_document.h
Normal file
34
src/app/crash/read_document.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_CRASH_READ_DOCUMENT_H_INCLUDED
|
||||
#define APP_CRASH_READ_DOCUMENT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/frame.h"
|
||||
#include "doc/pixel_format.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
class Document;
|
||||
namespace crash {
|
||||
|
||||
struct DocumentInfo {
|
||||
doc::PixelFormat format;
|
||||
int width;
|
||||
int height;
|
||||
doc::frame_t frames;
|
||||
};
|
||||
|
||||
bool read_document_info(const std::string& dir, DocumentInfo& info);
|
||||
app::Document* read_document(const std::string& dir);
|
||||
|
||||
} // namespace crash
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -11,21 +11,41 @@
|
||||
|
||||
#include "app/crash/session.h"
|
||||
|
||||
#include "app/console.h"
|
||||
#include "app/context.h"
|
||||
#include "app/crash/document_io.h"
|
||||
#include "app/crash/read_document.h"
|
||||
#include "app/crash/write_document.h"
|
||||
#include "app/document.h"
|
||||
#include "app/document_access.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/process.h"
|
||||
#include "base/split_string.h"
|
||||
#include "base/string.h"
|
||||
|
||||
namespace app {
|
||||
namespace crash {
|
||||
|
||||
Session::Backup::Backup(const std::string& dir)
|
||||
: m_dir(dir)
|
||||
{
|
||||
DocumentInfo info;
|
||||
read_document_info(dir, info);
|
||||
|
||||
std::vector<char> buf(1024);
|
||||
sprintf(&buf[0], "%s Sprite %dx%d with %d frame(s)",
|
||||
info.format == IMAGE_RGB ? "RGB":
|
||||
info.format == IMAGE_GRAYSCALE ? "Grayscale":
|
||||
info.format == IMAGE_INDEXED ? "Indexed":
|
||||
info.format == IMAGE_BITMAP ? "Bitmap": "Unknown",
|
||||
info.width, info.height, info.frames);
|
||||
m_desc = &buf[0];
|
||||
}
|
||||
|
||||
Session::Session(const std::string& path)
|
||||
: m_path(path)
|
||||
, m_pid(0)
|
||||
@ -36,6 +56,32 @@ Session::~Session()
|
||||
{
|
||||
}
|
||||
|
||||
std::string Session::name() const
|
||||
{
|
||||
std::string name = base::get_file_title(m_path);
|
||||
std::vector<std::string> parts;
|
||||
base::split_string(name, parts, "-");
|
||||
|
||||
if (parts.size() == 3) {
|
||||
return "Session date: " + parts[0] + " time: " + parts[1] + " (PID " + parts[2] + ")";
|
||||
}
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
const Session::Backups& Session::backups()
|
||||
{
|
||||
if (m_backups.empty()) {
|
||||
for (auto& item : base::list_files(m_path)) {
|
||||
std::string docDir = base::join_path(m_path, item);
|
||||
if (base::is_directory(docDir)) {
|
||||
m_backups.push_back(new Backup(docDir));
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_backups;
|
||||
}
|
||||
|
||||
bool Session::isRunning()
|
||||
{
|
||||
loadPid();
|
||||
@ -45,7 +91,7 @@ bool Session::isRunning()
|
||||
bool Session::isEmpty()
|
||||
{
|
||||
for (auto& item : base::list_files(m_path)) {
|
||||
if (base::is_directory(item))
|
||||
if (base::is_directory(base::join_path(m_path, item)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -71,8 +117,9 @@ void Session::removeFromDisk()
|
||||
try {
|
||||
base::remove_directory(m_path);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
// Is not empty
|
||||
catch (const std::exception& ex) {
|
||||
TRACE("Session directory cannot be removed, it's not empty\nError: '%s'\n",
|
||||
ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,6 +139,46 @@ void Session::saveDocumentChanges(app::Document* doc)
|
||||
write_document(dir, doc);
|
||||
}
|
||||
|
||||
void Session::removeDocument(app::Document* doc)
|
||||
{
|
||||
delete_document_internals(doc);
|
||||
|
||||
// Delete document backup directory
|
||||
std::string dir = base::join_path(m_path,
|
||||
base::convert_to<std::string>(doc->id()));
|
||||
if (base::is_directory(dir))
|
||||
deleteDirectory(dir);
|
||||
}
|
||||
|
||||
void Session::restoreBackup(Backup* backup)
|
||||
{
|
||||
Console console;
|
||||
try {
|
||||
app::Document* doc = read_document(backup->dir());
|
||||
if (doc)
|
||||
UIContext::instance()->documents().add(doc);
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
void Session::deleteBackup(Backup* backup)
|
||||
{
|
||||
try {
|
||||
auto it = std::find(m_backups.begin(), m_backups.end(), backup);
|
||||
ASSERT(it != m_backups.end());
|
||||
if (it != m_backups.end())
|
||||
m_backups.erase(it);
|
||||
|
||||
if (base::is_directory(backup->dir()))
|
||||
deleteDirectory(backup->dir());
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
void Session::loadPid()
|
||||
{
|
||||
if (m_pid)
|
||||
@ -110,9 +197,14 @@ std::string Session::pidFilename() const
|
||||
return base::join_path(m_path, "pid");
|
||||
}
|
||||
|
||||
std::string Session::dataFilename() const
|
||||
void Session::deleteDirectory(const std::string& dir)
|
||||
{
|
||||
return base::join_path(m_path, "data");
|
||||
for (auto& item : base::list_files(dir)) {
|
||||
std::string objfn = base::join_path(dir, item);
|
||||
if (base::is_file(objfn))
|
||||
base::delete_file(objfn);
|
||||
}
|
||||
base::remove_directory(dir);
|
||||
}
|
||||
|
||||
} // namespace crash
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
class Document;
|
||||
@ -24,9 +25,23 @@ namespace crash {
|
||||
// A class to record/restore session information.
|
||||
class Session {
|
||||
public:
|
||||
class Backup {
|
||||
public:
|
||||
Backup(const std::string& dir);
|
||||
const std::string& dir() const { return m_dir; }
|
||||
const std::string& description() const { return m_desc; }
|
||||
private:
|
||||
std::string m_dir;
|
||||
std::string m_desc;
|
||||
};
|
||||
typedef std::vector<Backup*> Backups;
|
||||
|
||||
Session(const std::string& path);
|
||||
~Session();
|
||||
|
||||
std::string name() const;
|
||||
const Backups& backups();
|
||||
|
||||
bool isRunning();
|
||||
bool isEmpty();
|
||||
|
||||
@ -34,16 +49,21 @@ namespace crash {
|
||||
void removeFromDisk();
|
||||
|
||||
void saveDocumentChanges(app::Document* doc);
|
||||
void removeDocument(app::Document* doc);
|
||||
|
||||
void restoreBackup(Backup* backup);
|
||||
void deleteBackup(Backup* backup);
|
||||
|
||||
private:
|
||||
void loadPid();
|
||||
std::string pidFilename() const;
|
||||
std::string dataFilename() const;
|
||||
void deleteDirectory(const std::string& dir);
|
||||
|
||||
base::pid m_pid;
|
||||
std::string m_path;
|
||||
std::fstream m_log;
|
||||
std::fstream m_pidFile;
|
||||
Backups m_backups;
|
||||
|
||||
DISABLE_COPYING(Session);
|
||||
};
|
||||
|
@ -9,13 +9,15 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/crash/document_io.h"
|
||||
#include "app/crash/write_document.h"
|
||||
|
||||
#include "app/document.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/serialization.h"
|
||||
#include "base/string.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/cel_data_io.h"
|
||||
#include "doc/cel_io.h"
|
||||
@ -41,11 +43,11 @@ using namespace base::serialization::little_endian;
|
||||
using namespace doc;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define OFSTREAM(name, fn) \
|
||||
std::ofstream name(base::from_utf8(base::join_path(path, fn)));
|
||||
#define OFSTREAM(dir, name, fn) \
|
||||
std::ofstream name(base::from_utf8(base::join_path(dir, fn)), std::ofstream::binary);
|
||||
#else
|
||||
#define OFSTREAM(name, fn) \
|
||||
std::ofstream name(base::join_path(path, fn));
|
||||
#define OFSTREAM(dir, name, fn) \
|
||||
std::ofstream name(base::join_path(dir, fn), std::ofstream::binary);
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@ -53,29 +55,56 @@ namespace {
|
||||
typedef std::map<ObjectId, ObjectVersion> Versions;
|
||||
static std::map<ObjectId, Versions> g_documentObjects;
|
||||
|
||||
template<typename T, typename WriteFunc>
|
||||
void save_object(const char* prefix, T* obj, WriteFunc write_func, Versions& versions, const std::string& dir)
|
||||
{
|
||||
if (!obj->version())
|
||||
obj->incrementVersion();
|
||||
|
||||
if (versions[obj->id()] != obj->version()) {
|
||||
OFSTREAM(dir, s, prefix + base::convert_to<std::string>(obj->id()));
|
||||
write_func(s, obj);
|
||||
versions[obj->id()] = obj->version();
|
||||
|
||||
TRACE(" - %s %d saved with version %d\n",
|
||||
prefix, obj->id(), obj->version());
|
||||
}
|
||||
else {
|
||||
TRACE(" - Ignoring %s %d (version %d already saved)\n",
|
||||
prefix, obj->id(), obj->version());
|
||||
}
|
||||
}
|
||||
|
||||
void write_sprite(std::ofstream& s, Sprite* spr)
|
||||
{
|
||||
write32(s, spr->id());
|
||||
write32(s, spr->version());
|
||||
write32(s, spr->folder()->version());
|
||||
write32(s, spr->pixelFormat());
|
||||
write32(s, spr->width());
|
||||
write32(s, spr->height());
|
||||
write8(s, spr->pixelFormat());
|
||||
write16(s, spr->width());
|
||||
write16(s, spr->height());
|
||||
write32(s, spr->transparentColor());
|
||||
|
||||
// Frame durations
|
||||
write32(s, spr->totalFrames());
|
||||
for (frame_t fr = 0; fr < spr->totalFrames(); ++fr)
|
||||
write32(s, spr->frameDuration(fr));
|
||||
|
||||
// IDs of all main layers
|
||||
std::vector<Layer*> layers;
|
||||
spr->getLayersList(layers);
|
||||
write32(s, layers.size());
|
||||
for (Layer* lay : layers)
|
||||
write32(s, lay->id());
|
||||
|
||||
// IDs of all palettes
|
||||
write32(s, spr->getPalettes().size());
|
||||
for (Palette* pal : spr->getPalettes())
|
||||
write32(s, pal->id());
|
||||
}
|
||||
|
||||
void write_layer_structure(std::ofstream& s, Layer* lay)
|
||||
{
|
||||
write32(s, lay->id());
|
||||
write_string(s, lay->name());
|
||||
|
||||
write32(s, static_cast<int>(lay->flags())); // Flags
|
||||
write16(s, static_cast<int>(lay->type())); // Type
|
||||
write_string(s, lay->name());
|
||||
|
||||
if (lay->type() == ObjectType::LayerImage) {
|
||||
CelConstIterator it, begin = static_cast<const LayerImage*>(lay)->getCelBegin();
|
||||
@ -90,58 +119,58 @@ void write_layer_structure(std::ofstream& s, Layer* lay)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename WriteFunc>
|
||||
void save_object(T* obj, const char* prefix, WriteFunc write_func, Versions& versions, const std::string& path)
|
||||
{
|
||||
if (!obj->version())
|
||||
obj->incrementVersion();
|
||||
|
||||
if (versions[obj->id()] != obj->version()) {
|
||||
OFSTREAM(s, prefix + base::convert_to<std::string>(obj->id()));
|
||||
write_func(s, obj);
|
||||
versions[obj->id()] = obj->version();
|
||||
|
||||
TRACE(" - %s %d saved with version %d\n",
|
||||
prefix, obj->id(), obj->version());
|
||||
}
|
||||
else {
|
||||
TRACE(" - Ignoring %s %d (version %d already saved)\n",
|
||||
prefix, obj->id(), obj->version());
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void write_document(const std::string& path, app::Document* doc)
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Public API
|
||||
|
||||
void write_document(const std::string& dir, app::Document* doc)
|
||||
{
|
||||
Versions& versions = g_documentObjects[doc->id()];
|
||||
|
||||
Sprite* spr = doc->sprite();
|
||||
save_object(spr, "spr", write_sprite, versions, path);
|
||||
|
||||
// Create a "doc" file with the main sprite ID
|
||||
if (!base::is_file(base::join_path(dir, "doc"))) {
|
||||
OFSTREAM(dir, s, "doc");
|
||||
write32(s, spr->id());
|
||||
}
|
||||
|
||||
save_object("spr", spr, write_sprite, versions, dir);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Create one stream for each object
|
||||
|
||||
for (Palette* pal : spr->getPalettes())
|
||||
save_object(pal, "pal", write_palette, versions, path);
|
||||
save_object("pal", pal, write_palette, versions, dir);
|
||||
|
||||
for (FrameTag* frtag : spr->frameTags())
|
||||
save_object(frtag, "frtag", write_frame_tag, versions, path);
|
||||
save_object("frtag", frtag, write_frame_tag, versions, dir);
|
||||
|
||||
std::vector<Layer*> layers;
|
||||
spr->getLayersList(layers);
|
||||
for (Layer* lay : layers)
|
||||
save_object(lay, "lay", write_layer_structure, versions, path);
|
||||
save_object("lay", lay, write_layer_structure, versions, dir);
|
||||
|
||||
for (Cel* cel : spr->cels())
|
||||
save_object(cel, "cel", write_cel, versions, path);
|
||||
save_object("cel", cel, write_cel, versions, dir);
|
||||
|
||||
// Images (CelData)
|
||||
for (Cel* cel : spr->uniqueCels()) {
|
||||
save_object(cel->data(), "celdata", write_celdata, versions, path);
|
||||
save_object(cel->image(), "img", write_image, versions, path);
|
||||
save_object("celdata", cel->data(), write_celdata, versions, dir);
|
||||
save_object("img", cel->image(), write_image, versions, dir);
|
||||
}
|
||||
}
|
||||
|
||||
void delete_document_internals(app::Document* doc)
|
||||
{
|
||||
ASSERT(doc);
|
||||
auto it = g_documentObjects.find(doc->id());
|
||||
|
||||
// The document could not be inside g_documentObjects in case it was
|
||||
// never saved by the backup process.
|
||||
if (it != g_documentObjects.end())
|
||||
g_documentObjects.erase(it);
|
||||
}
|
||||
|
||||
} // namespace crash
|
||||
} // namespace app
|
@ -16,6 +16,7 @@ class Document;
|
||||
namespace crash {
|
||||
|
||||
void write_document(const std::string& dir, app::Document* doc);
|
||||
void delete_document_internals(app::Document* doc);
|
||||
|
||||
} // namespace crash
|
||||
} // namespace app
|
152
src/app/ui/data_recovery_view.cpp
Normal file
152
src/app/ui/data_recovery_view.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/ui/data_recovery_view.h"
|
||||
|
||||
#include "app/app_menus.h"
|
||||
#include "app/crash/data_recovery.h"
|
||||
#include "app/crash/session.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/ui/skin/skin_style_property.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/workspace.h"
|
||||
#include "base/bind.h"
|
||||
#include "ui/button.h"
|
||||
#include "ui/entry.h"
|
||||
#include "ui/listitem.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/resize_event.h"
|
||||
#include "ui/system.h"
|
||||
#include "ui/view.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
using namespace app::skin;
|
||||
|
||||
class BackupItem : public ListItem {
|
||||
public:
|
||||
BackupItem(crash::Session* session, crash::Session::Backup* backup)
|
||||
: ListItem(" - " + backup->description())
|
||||
, m_session(session)
|
||||
, m_backup(backup)
|
||||
, m_openButton("Open")
|
||||
, m_deleteButton("Delete")
|
||||
, m_restored(false)
|
||||
{
|
||||
addChild(&m_openButton);
|
||||
addChild(&m_deleteButton);
|
||||
|
||||
m_openButton.Click.connect(Bind(&BackupItem::onOpen, this));
|
||||
m_deleteButton.Click.connect(Bind(&BackupItem::onDelete, this));
|
||||
|
||||
setup_mini_look(&m_openButton);
|
||||
setup_mini_look(&m_deleteButton);
|
||||
}
|
||||
|
||||
protected:
|
||||
void onResize(ResizeEvent& ev) override {
|
||||
ListItem::onResize(ev);
|
||||
|
||||
gfx::Rect rc = ev.getBounds();
|
||||
gfx::Size sz1 = m_openButton.getPreferredSize();
|
||||
gfx::Size sz2 = m_deleteButton.getPreferredSize();
|
||||
m_openButton.setBounds(gfx::Rect(rc.x+rc.w-sz2.w-sz1.w, rc.y, sz1.w, rc.h));
|
||||
m_deleteButton.setBounds(gfx::Rect(rc.x+rc.w-sz2.w, rc.y, sz2.w, rc.h));
|
||||
}
|
||||
|
||||
void onOpen() {
|
||||
if (!m_restored) {
|
||||
m_restored = true;
|
||||
setText("RESTORED: " + getText());
|
||||
}
|
||||
|
||||
m_session->restoreBackup(m_backup);
|
||||
}
|
||||
|
||||
void onDelete() {
|
||||
m_session->deleteBackup(m_backup);
|
||||
|
||||
getParent()->invalidate();
|
||||
deferDelete();
|
||||
}
|
||||
|
||||
private:
|
||||
crash::Session* m_session;
|
||||
crash::Session::Backup* m_backup;
|
||||
ui::Button m_openButton;
|
||||
ui::Button m_deleteButton;
|
||||
bool m_restored;
|
||||
};
|
||||
|
||||
DataRecoveryView::DataRecoveryView(crash::DataRecovery* dataRecovery)
|
||||
: Box(JI_VERTICAL)
|
||||
, m_dataRecovery(dataRecovery)
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
||||
setBgColor(theme->colors.workspace());
|
||||
|
||||
addChild(&m_view);
|
||||
m_view.setExpansive(true);
|
||||
m_view.attachToView(&m_listBox);
|
||||
|
||||
for (auto& session : m_dataRecovery->sessions()) {
|
||||
m_listBox.addChild(new ListItem(session->name()));
|
||||
|
||||
for (auto& backup : session->backups())
|
||||
m_listBox.addChild(new BackupItem(session.get(), backup));
|
||||
}
|
||||
}
|
||||
|
||||
DataRecoveryView::~DataRecoveryView()
|
||||
{
|
||||
}
|
||||
|
||||
std::string DataRecoveryView::getTabText()
|
||||
{
|
||||
return "Data Recovery";
|
||||
}
|
||||
|
||||
TabIcon DataRecoveryView::getTabIcon()
|
||||
{
|
||||
return TabIcon::NONE;
|
||||
}
|
||||
|
||||
WorkspaceView* DataRecoveryView::cloneWorkspaceView()
|
||||
{
|
||||
return nullptr; // This view cannot be cloned
|
||||
}
|
||||
|
||||
void DataRecoveryView::onWorkspaceViewSelected()
|
||||
{
|
||||
}
|
||||
|
||||
void DataRecoveryView::onClonedFrom(WorkspaceView* from)
|
||||
{
|
||||
ASSERT(false); // Never called
|
||||
}
|
||||
|
||||
bool DataRecoveryView::onCloseView(Workspace* workspace)
|
||||
{
|
||||
workspace->removeView(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataRecoveryView::onTabPopup(Workspace* workspace)
|
||||
{
|
||||
Menu* menu = AppMenus::instance()->getTabPopupMenu();
|
||||
if (!menu)
|
||||
return;
|
||||
|
||||
menu->showPopup(ui::get_mouse_position());
|
||||
}
|
||||
|
||||
} // namespace app
|
50
src/app/ui/data_recovery_view.h
Normal file
50
src/app/ui/data_recovery_view.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_UI_DATA_RECOVERY_VIEW_H_INCLUDED
|
||||
#define APP_UI_DATA_RECOVERY_VIEW_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/tabs.h"
|
||||
#include "app/ui/workspace_view.h"
|
||||
#include "ui/box.h"
|
||||
#include "ui/listbox.h"
|
||||
#include "ui/view.h"
|
||||
|
||||
namespace app {
|
||||
namespace crash {
|
||||
class DataRecovery;
|
||||
}
|
||||
|
||||
class DataRecoveryView : public ui::Box
|
||||
, public TabView
|
||||
, public WorkspaceView {
|
||||
public:
|
||||
DataRecoveryView(crash::DataRecovery* dataRecovery);
|
||||
~DataRecoveryView();
|
||||
|
||||
// TabView implementation
|
||||
std::string getTabText() override;
|
||||
TabIcon getTabIcon() override;
|
||||
|
||||
// WorkspaceView implementation
|
||||
ui::Widget* getContentWidget() override { return this; }
|
||||
WorkspaceView* cloneWorkspaceView() override;
|
||||
void onWorkspaceViewSelected() override;
|
||||
void onClonedFrom(WorkspaceView* from) override;
|
||||
bool onCloseView(Workspace* workspace) override;
|
||||
void onTabPopup(Workspace* workspace) override;
|
||||
|
||||
private:
|
||||
crash::DataRecovery* m_dataRecovery;
|
||||
ui::View m_view;
|
||||
ui::ListBox m_listBox;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -11,14 +11,18 @@
|
||||
|
||||
#include "app/ui/home_view.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/app_menus.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/ui/data_recovery_view.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui/news_listbox.h"
|
||||
#include "app/ui/recent_listbox.h"
|
||||
#include "app/ui/skin/skin_style_property.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/workspace.h"
|
||||
#include "app/ui/workspace_tabs.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/exception.h"
|
||||
@ -37,6 +41,8 @@ HomeView::HomeView()
|
||||
: m_files(new RecentFilesListBox)
|
||||
, m_folders(new RecentFoldersListBox)
|
||||
, m_news(new NewsListBox)
|
||||
, m_dataRecovery(nullptr)
|
||||
, m_dataRecoveryView(nullptr)
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
||||
setBgColor(theme->colors.workspace());
|
||||
@ -45,16 +51,29 @@ HomeView::HomeView()
|
||||
|
||||
newFile()->Click.connect(Bind(&HomeView::onNewFile, this));
|
||||
openFile()->Click.connect(Bind(&HomeView::onOpenFile, this));
|
||||
recoverSprites()->Click.connect(Bind(&HomeView::onRecoverSprites, this));
|
||||
|
||||
filesView()->attachToView(m_files);
|
||||
foldersView()->attachToView(m_folders);
|
||||
newsView()->attachToView(m_news);
|
||||
|
||||
checkUpdate()->setVisible(false);
|
||||
recoverSpritesPlaceholder()->setVisible(false);
|
||||
}
|
||||
|
||||
HomeView::~HomeView()
|
||||
{
|
||||
if (m_dataRecoveryView) {
|
||||
if (m_dataRecoveryView->getParent())
|
||||
App::instance()->getMainWindow()->getWorkspace()->removeView(m_dataRecoveryView);
|
||||
delete m_dataRecoveryView;
|
||||
}
|
||||
}
|
||||
|
||||
void HomeView::showDataRecovery(crash::DataRecovery* dataRecovery)
|
||||
{
|
||||
m_dataRecovery = dataRecovery;
|
||||
recoverSpritesPlaceholder()->setVisible(true);
|
||||
}
|
||||
|
||||
std::string HomeView::getTabText()
|
||||
@ -153,4 +172,15 @@ void HomeView::onNewUpdate(const std::string& url, const std::string& version)
|
||||
layout();
|
||||
}
|
||||
|
||||
void HomeView::onRecoverSprites()
|
||||
{
|
||||
if (!m_dataRecoveryView)
|
||||
m_dataRecoveryView = new DataRecoveryView(m_dataRecovery);
|
||||
|
||||
if (!m_dataRecoveryView->getParent())
|
||||
App::instance()->getMainWindow()->getWorkspace()->addView(m_dataRecoveryView);
|
||||
|
||||
App::instance()->getMainWindow()->getTabsBar()->selectTab(m_dataRecoveryView);
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -22,10 +22,15 @@ namespace ui {
|
||||
|
||||
namespace app {
|
||||
|
||||
class DataRecoveryView;
|
||||
class NewsListBox;
|
||||
class RecentFilesListBox;
|
||||
class RecentFoldersListBox;
|
||||
|
||||
namespace crash {
|
||||
class DataRecovery;
|
||||
}
|
||||
|
||||
class HomeView : public app::gen::HomeView
|
||||
, public TabView
|
||||
, public WorkspaceView
|
||||
@ -37,6 +42,8 @@ namespace app {
|
||||
HomeView();
|
||||
~HomeView();
|
||||
|
||||
void showDataRecovery(crash::DataRecovery* dataRecovery);
|
||||
|
||||
// TabView implementation
|
||||
std::string getTabText() override;
|
||||
TabIcon getTabIcon() override;
|
||||
@ -61,10 +68,13 @@ namespace app {
|
||||
private:
|
||||
void onNewFile();
|
||||
void onOpenFile();
|
||||
void onRecoverSprites();
|
||||
|
||||
RecentFilesListBox* m_files;
|
||||
RecentFoldersListBox* m_folders;
|
||||
NewsListBox* m_news;
|
||||
crash::DataRecovery* m_dataRecovery;
|
||||
DataRecoveryView* m_dataRecoveryView;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -222,6 +222,11 @@ void MainWindow::popTimeline()
|
||||
setTimelineVisibility(true);
|
||||
}
|
||||
|
||||
void MainWindow::showDataRecovery(crash::DataRecovery* dataRecovery)
|
||||
{
|
||||
getHomeView()->showDataRecovery(dataRecovery);
|
||||
}
|
||||
|
||||
bool MainWindow::onProcessMessage(ui::Message* msg)
|
||||
{
|
||||
if (msg->type() == kOpenMessage)
|
||||
|
@ -38,6 +38,10 @@ namespace app {
|
||||
class Workspace;
|
||||
class WorkspaceTabs;
|
||||
|
||||
namespace crash {
|
||||
class DataRecovery;
|
||||
}
|
||||
|
||||
class MainWindow : public app::gen::MainWindow
|
||||
, public TabsDelegate {
|
||||
public:
|
||||
@ -73,6 +77,8 @@ namespace app {
|
||||
void setTimelineVisibility(bool visible);
|
||||
void popTimeline();
|
||||
|
||||
void showDataRecovery(crash::DataRecovery* dataRecovery);
|
||||
|
||||
// TabsDelegate implementation.
|
||||
bool onIsModified(Tabs* tabs, TabView* tabView) override;
|
||||
void onSelectTab(Tabs* tabs, TabView* tabView) override;
|
||||
|
@ -25,26 +25,26 @@ using namespace base::serialization::little_endian;
|
||||
void write_celdata(std::ostream& os, const CelData* celdata)
|
||||
{
|
||||
write32(os, celdata->id());
|
||||
write16(os, (int16_t)celdata->position().x);
|
||||
write16(os, (int16_t)celdata->position().y);
|
||||
write32(os, (int16_t)celdata->position().x);
|
||||
write32(os, (int16_t)celdata->position().y);
|
||||
write8(os, celdata->opacity());
|
||||
write32(os, celdata->image()->id());
|
||||
}
|
||||
|
||||
CelData* read_celdata(std::istream& is, SubObjectsIO* subObjects)
|
||||
CelData* read_celdata(std::istream& is, SubObjectsIO* subObjects, bool setId)
|
||||
{
|
||||
ObjectId id = read32(is);
|
||||
int x = (int16_t)read16(is);
|
||||
int y = (int16_t)read16(is);
|
||||
int x = read32(is);
|
||||
int y = read32(is);
|
||||
int opacity = read8(is);
|
||||
|
||||
ObjectId imageId = read32(is);
|
||||
ImageRef image(subObjects->getImageRef(imageId));
|
||||
|
||||
base::UniquePtr<CelData> celdata(new CelData(image));
|
||||
celdata->setPosition(x, y);
|
||||
celdata->setOpacity(opacity);
|
||||
celdata->setId(id);
|
||||
if (setId)
|
||||
celdata->setId(id);
|
||||
return celdata.release();
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace doc {
|
||||
class SubObjectsIO;
|
||||
|
||||
void write_celdata(std::ostream& os, const CelData* cel);
|
||||
CelData* read_celdata(std::istream& is, SubObjectsIO* subObjects);
|
||||
CelData* read_celdata(std::istream& is, SubObjectsIO* subObjects, bool setId = true);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
|
@ -29,7 +29,7 @@ void write_cel(std::ostream& os, const Cel* cel)
|
||||
write32(os, cel->dataRef()->id());
|
||||
}
|
||||
|
||||
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects)
|
||||
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects, bool setId)
|
||||
{
|
||||
ObjectId id = read32(is);
|
||||
frame_t frame(read16(is));
|
||||
@ -38,7 +38,8 @@ Cel* read_cel(std::istream& is, SubObjectsIO* subObjects)
|
||||
ASSERT(celData);
|
||||
|
||||
base::UniquePtr<Cel> cel(new Cel(frame, celData));
|
||||
cel->setId(id);
|
||||
if (setId)
|
||||
cel->setId(id);
|
||||
return cel.release();
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace doc {
|
||||
class SubObjectsIO;
|
||||
|
||||
void write_cel(std::ostream& os, const Cel* cel);
|
||||
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects);
|
||||
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects, bool setId = true);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
|
@ -46,7 +46,7 @@ void write_image(std::ostream& os, const Image* image)
|
||||
os.write((char*)image->getPixelAddress(0, c), size);
|
||||
}
|
||||
|
||||
Image* read_image(std::istream& is)
|
||||
Image* read_image(std::istream& is, bool setId)
|
||||
{
|
||||
ObjectId id = read32(is);
|
||||
int pixelFormat = read8(is); // Pixel format
|
||||
@ -61,7 +61,8 @@ Image* read_image(std::istream& is)
|
||||
is.read((char*)image->getPixelAddress(0, c), size);
|
||||
|
||||
image->setMaskColor(maskColor);
|
||||
image->setId(id);
|
||||
if (setId)
|
||||
image->setId(id);
|
||||
return image.release();
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace doc {
|
||||
class Image;
|
||||
|
||||
void write_image(std::ostream& os, const Image* image);
|
||||
Image* read_image(std::istream& is);
|
||||
Image* read_image(std::istream& is, bool setId = true);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
|
@ -96,7 +96,7 @@ void write_layer(std::ostream& os, const Layer* layer)
|
||||
}
|
||||
}
|
||||
|
||||
Layer* read_layer(std::istream& is, SubObjectsIO* subObjects)
|
||||
Layer* read_layer(std::istream& is, SubObjectsFromSprite* subObjects)
|
||||
{
|
||||
ObjectId id = read32(is);
|
||||
std::string name = read_string(is);
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
namespace doc {
|
||||
class Layer;
|
||||
class SubObjectsIO;
|
||||
class SubObjectsFromSprite;
|
||||
|
||||
// Thrown when a invalid layer type is read from the istream.
|
||||
class InvalidLayerType : public base::Exception {
|
||||
@ -23,7 +23,7 @@ namespace doc {
|
||||
};
|
||||
|
||||
void write_layer(std::ostream& os, const Layer* layer);
|
||||
Layer* read_layer(std::istream& is, SubObjectsIO* subObjects);
|
||||
Layer* read_layer(std::istream& is, SubObjectsFromSprite* subObjects);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
|
@ -22,19 +22,19 @@ namespace doc {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
SubObjectsIO::SubObjectsIO(Sprite* sprite)
|
||||
SubObjectsFromSprite::SubObjectsFromSprite(Sprite* sprite)
|
||||
: m_sprite(sprite)
|
||||
{
|
||||
}
|
||||
|
||||
void SubObjectsIO::addImageRef(const ImageRef& image)
|
||||
void SubObjectsFromSprite::addImageRef(const ImageRef& image)
|
||||
{
|
||||
ASSERT(image);
|
||||
ASSERT(!getImageRef(image->id()));
|
||||
m_images.insert(std::make_pair(image->id(), image));
|
||||
}
|
||||
|
||||
ImageRef SubObjectsIO::getImageRef(ObjectId imageId)
|
||||
ImageRef SubObjectsFromSprite::getImageRef(ObjectId imageId)
|
||||
{
|
||||
auto it = m_images.find(imageId);
|
||||
if (it != m_images.end()) {
|
||||
@ -47,14 +47,14 @@ ImageRef SubObjectsIO::getImageRef(ObjectId imageId)
|
||||
return m_sprite->getImageRef(imageId);
|
||||
}
|
||||
|
||||
void SubObjectsIO::addCelDataRef(const CelDataRef& celdata)
|
||||
void SubObjectsFromSprite::addCelDataRef(const CelDataRef& celdata)
|
||||
{
|
||||
ASSERT(celdata);
|
||||
ASSERT(!getCelDataRef(celdata->id()));
|
||||
m_celdatas.insert(std::make_pair(celdata->id(), celdata));
|
||||
}
|
||||
|
||||
CelDataRef SubObjectsIO::getCelDataRef(ObjectId celdataId)
|
||||
CelDataRef SubObjectsFromSprite::getCelDataRef(ObjectId celdataId)
|
||||
{
|
||||
auto it = m_celdatas.find(celdataId);
|
||||
if (it != m_celdatas.end()) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -17,18 +17,25 @@
|
||||
namespace doc {
|
||||
class Sprite;
|
||||
|
||||
// Helper class used to read children-objects by layers and cels.
|
||||
class SubObjectsIO {
|
||||
public:
|
||||
SubObjectsIO(Sprite* sprite);
|
||||
virtual ~SubObjectsIO() { }
|
||||
virtual ImageRef getImageRef(ObjectId imageId) = 0;
|
||||
virtual CelDataRef getCelDataRef(ObjectId celdataId) = 0;
|
||||
};
|
||||
|
||||
// Helper class used to read children-objects by layers and cels.
|
||||
class SubObjectsFromSprite : public SubObjectsIO {
|
||||
public:
|
||||
SubObjectsFromSprite(Sprite* sprite);
|
||||
|
||||
Sprite* sprite() const { return m_sprite; }
|
||||
|
||||
void addImageRef(const ImageRef& image);
|
||||
void addCelDataRef(const CelDataRef& celdata);
|
||||
|
||||
ImageRef getImageRef(ObjectId imageId);
|
||||
CelDataRef getCelDataRef(ObjectId celdataId);
|
||||
ImageRef getImageRef(ObjectId imageId) override;
|
||||
CelDataRef getCelDataRef(ObjectId celdataId) override;
|
||||
|
||||
private:
|
||||
Sprite* m_sprite;
|
||||
|
Loading…
x
Reference in New Issue
Block a user