2015-04-08 01:51:10 +08:00
|
|
|
// 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
|
|
|
|
|
2015-04-09 07:05:05 +08:00
|
|
|
#include "app/crash/write_document.h"
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
#include "app/crash/internals.h"
|
2015-04-08 01:51:10 +08:00
|
|
|
#include "app/document.h"
|
|
|
|
#include "base/convert_to.h"
|
2015-04-09 07:05:05 +08:00
|
|
|
#include "base/fs.h"
|
2015-05-21 02:34:36 +08:00
|
|
|
#include "base/fstream_path.h"
|
2015-04-08 01:51:10 +08:00
|
|
|
#include "base/path.h"
|
|
|
|
#include "base/serialization.h"
|
|
|
|
#include "base/string.h"
|
2015-04-09 07:05:05 +08:00
|
|
|
#include "base/unique_ptr.h"
|
2015-04-08 01:51:10 +08:00
|
|
|
#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 <fstream>
|
2015-04-08 04:53:31 +08:00
|
|
|
#include <map>
|
2015-04-08 01:51:10 +08:00
|
|
|
|
|
|
|
namespace app {
|
|
|
|
namespace crash {
|
|
|
|
|
|
|
|
using namespace base::serialization;
|
|
|
|
using namespace base::serialization::little_endian;
|
|
|
|
using namespace doc;
|
|
|
|
|
2015-04-08 04:53:31 +08:00
|
|
|
namespace {
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
static std::map<ObjectId, ObjVersionsMap> g_docVersions;
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
class Writer {
|
|
|
|
public:
|
|
|
|
Writer(const std::string& dir, app::Document* doc)
|
|
|
|
: m_dir(dir)
|
|
|
|
, m_doc(doc)
|
2015-04-23 03:46:57 +08:00
|
|
|
, m_objVersions(g_docVersions[doc->id()]) {
|
2015-04-22 03:52:41 +08:00
|
|
|
}
|
2015-04-09 07:05:05 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void saveDocument() {
|
|
|
|
Sprite* spr = m_doc->sprite();
|
2015-04-09 07:05:05 +08:00
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
// Save from objects without children (e.g. images), to aggregated
|
|
|
|
// objects (e.g. cels, layers, etc.)
|
2015-04-09 08:32:41 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
for (Palette* pal : spr->getPalettes())
|
|
|
|
saveObject("pal", pal, &Writer::writePalette);
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
for (FrameTag* frtag : spr->frameTags())
|
|
|
|
saveObject("frtag", frtag, &Writer::writeFrameTag);
|
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
for (Cel* cel : spr->uniqueCels()) {
|
|
|
|
saveObject("img", cel->image(), &Writer::writeImage);
|
|
|
|
saveObject("celdata", cel->data(), &Writer::writeCelData);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Cel* cel : spr->cels())
|
|
|
|
saveObject("cel", cel, &Writer::writeCel);
|
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
std::vector<Layer*> layers;
|
|
|
|
spr->getLayersList(layers);
|
|
|
|
for (Layer* lay : layers)
|
|
|
|
saveObject("lay", lay, &Writer::writeLayerStructure);
|
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
saveObject("spr", spr, &Writer::writeSprite);
|
|
|
|
saveObject("doc", m_doc, &Writer::writeDocumentFile);
|
2015-04-08 01:51:10 +08:00
|
|
|
}
|
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
private:
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writeDocumentFile(std::ofstream& s, app::Document* doc) {
|
|
|
|
write32(s, doc->sprite()->id());
|
|
|
|
write_string(s, doc->filename());
|
|
|
|
}
|
2015-04-09 07:05:05 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writeSprite(std::ofstream& s, Sprite* spr) {
|
|
|
|
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());
|
2015-06-11 04:52:29 +08:00
|
|
|
|
|
|
|
// IDs of all frame tags
|
|
|
|
write32(s, spr->frameTags().size());
|
|
|
|
for (FrameTag* frtag : spr->frameTags())
|
|
|
|
write32(s, frtag->id());
|
2015-04-22 03:52:41 +08:00
|
|
|
}
|
2015-04-09 07:05:05 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writeLayerStructure(std::ofstream& s, Layer* lay) {
|
|
|
|
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();
|
|
|
|
CelConstIterator end = static_cast<const LayerImage*>(lay)->getCelEnd();
|
|
|
|
|
|
|
|
// Cels
|
|
|
|
write32(s, static_cast<const LayerImage*>(lay)->getCelsCount());
|
|
|
|
for (it=begin; it != end; ++it) {
|
|
|
|
const Cel* cel = *it;
|
|
|
|
write32(s, cel->id());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writeCel(std::ofstream& s, Cel* cel) {
|
|
|
|
write_cel(s, cel);
|
|
|
|
}
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writeCelData(std::ofstream& s, CelData* celdata) {
|
|
|
|
write_celdata(s, celdata);
|
|
|
|
}
|
2015-04-08 04:53:31 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writeImage(std::ofstream& s, Image* img) {
|
|
|
|
write_image(s, img);
|
|
|
|
}
|
2015-04-08 04:53:31 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writePalette(std::ofstream& s, Palette* pal) {
|
|
|
|
write_palette(s, pal);
|
|
|
|
}
|
2015-04-08 04:53:31 +08:00
|
|
|
|
2015-04-22 03:52:41 +08:00
|
|
|
void writeFrameTag(std::ofstream& s, FrameTag* frameTag) {
|
|
|
|
write_frame_tag(s, frameTag);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void saveObject(const char* prefix, T* obj, void (Writer::*writeMember)(std::ofstream&, T*)) {
|
|
|
|
if (!obj->version())
|
|
|
|
obj->incrementVersion();
|
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
ObjVersions& versions = m_objVersions[obj->id()];
|
|
|
|
if (versions.newer() == obj->version())
|
|
|
|
return;
|
2015-04-08 01:51:10 +08:00
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
std::string fn = prefix;
|
|
|
|
fn.push_back('-');
|
|
|
|
fn += base::convert_to<std::string>(obj->id());
|
2015-04-22 03:52:41 +08:00
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
std::string fullfn = base::join_path(m_dir, fn);
|
|
|
|
std::string oldfn = fullfn + "." + base::convert_to<std::string>(versions.older());
|
|
|
|
fullfn += "." + base::convert_to<std::string>(obj->version());
|
|
|
|
|
2015-05-21 02:34:36 +08:00
|
|
|
std::ofstream s(FSTREAM_PATH(fullfn), std::ofstream::binary);
|
2015-04-23 03:46:57 +08:00
|
|
|
write32(s, 0); // Leave a room for the magic number
|
|
|
|
(this->*writeMember)(s, obj); // Write the object
|
|
|
|
|
2015-06-11 07:02:44 +08:00
|
|
|
// Flush all data. In this way we ensure that the magic number is
|
|
|
|
// the last thing being written in the file.
|
|
|
|
s.flush();
|
|
|
|
|
2015-04-23 03:46:57 +08:00
|
|
|
// Write the magic number
|
|
|
|
s.seekp(0);
|
|
|
|
write32(s, MAGIC_NUMBER);
|
|
|
|
|
|
|
|
// Remove the older version
|
|
|
|
try {
|
|
|
|
if (versions.older() && base::is_file(oldfn))
|
|
|
|
base::delete_file(oldfn);
|
2015-04-22 03:52:41 +08:00
|
|
|
}
|
2015-04-23 03:46:57 +08:00
|
|
|
catch (const std::exception&) {
|
|
|
|
TRACE(" - Cannot delete %s #%d v%d\n", prefix, obj->id(), versions.older());
|
2015-04-22 03:52:41 +08:00
|
|
|
}
|
2015-04-23 03:46:57 +08:00
|
|
|
|
|
|
|
// Rotate versions and add the latest one
|
|
|
|
versions.rotateRevisions(obj->version());
|
|
|
|
|
|
|
|
TRACE(" - Saved %s #%d v%d\n", prefix, obj->id(), obj->version());
|
2015-04-08 01:51:10 +08:00
|
|
|
}
|
2015-04-22 03:52:41 +08:00
|
|
|
|
|
|
|
std::string m_dir;
|
|
|
|
app::Document* m_doc;
|
2015-04-23 03:46:57 +08:00
|
|
|
ObjVersionsMap& m_objVersions;
|
2015-04-22 03:52:41 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// Public API
|
|
|
|
|
|
|
|
void write_document(const std::string& dir, app::Document* doc)
|
|
|
|
{
|
|
|
|
Writer writer(dir, doc);
|
|
|
|
writer.saveDocument();
|
2015-04-08 01:51:10 +08:00
|
|
|
}
|
|
|
|
|
2015-04-09 07:05:05 +08:00
|
|
|
void delete_document_internals(app::Document* doc)
|
|
|
|
{
|
|
|
|
ASSERT(doc);
|
2015-04-23 03:46:57 +08:00
|
|
|
auto it = g_docVersions.find(doc->id());
|
2015-04-09 07:05:05 +08:00
|
|
|
|
|
|
|
// The document could not be inside g_documentObjects in case it was
|
|
|
|
// never saved by the backup process.
|
2015-04-23 03:46:57 +08:00
|
|
|
if (it != g_docVersions.end())
|
|
|
|
g_docVersions.erase(it);
|
2015-04-09 07:05:05 +08:00
|
|
|
}
|
|
|
|
|
2015-04-08 01:51:10 +08:00
|
|
|
} // namespace crash
|
|
|
|
} // namespace app
|