2022-07-11 00:16:20 -04:00
|
|
|
#include "object.h"
|
|
|
|
|
2022-07-11 17:19:36 -04:00
|
|
|
#include <iostream>
|
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
#include "othertypes.h"
|
2022-07-17 21:51:16 -04:00
|
|
|
#include "util.h"
|
|
|
|
|
2022-07-11 00:16:20 -04:00
|
|
|
namespace si {
|
|
|
|
|
|
|
|
Object::Object()
|
|
|
|
{
|
2022-07-11 17:19:36 -04:00
|
|
|
type_ = MxOb::Null;
|
|
|
|
id_ = 0;
|
2022-07-11 00:16:20 -04:00
|
|
|
}
|
|
|
|
|
2022-07-18 12:49:27 -04:00
|
|
|
#ifdef _WIN32
|
|
|
|
bool Object::ReplaceWithFile(const wchar_t *f)
|
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
File is;
|
|
|
|
if (!is.Open(f, File::Read)) {
|
2022-07-18 12:49:27 -04:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-18 14:25:00 -04:00
|
|
|
return ReplaceWithFile(&is);
|
2022-07-18 12:49:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Object::ExtractToFile(const wchar_t *f) const
|
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
File os;
|
|
|
|
if (!os.Open(f, File::Write)) {
|
2022-07-18 12:49:27 -04:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-18 14:25:00 -04:00
|
|
|
return ExtractToFile(&os);
|
2022-07-18 12:49:27 -04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
bool Object::ReplaceWithFile(const char *f)
|
2022-07-11 00:16:20 -04:00
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
File is;
|
|
|
|
if (!is.Open(f, File::Read)) {
|
2022-07-18 03:27:00 -04:00
|
|
|
return false;
|
2022-07-11 04:48:20 -04:00
|
|
|
}
|
2022-07-18 14:25:00 -04:00
|
|
|
return ReplaceWithFile(&is);
|
2022-07-18 03:27:00 -04:00
|
|
|
}
|
2022-07-11 04:48:20 -04:00
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
bool Object::ExtractToFile(const char *f) const
|
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
File os;
|
|
|
|
if (!os.Open(f, File::Write)) {
|
2022-07-18 03:27:00 -04:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-18 14:25:00 -04:00
|
|
|
return ExtractToFile(&os);
|
2022-07-11 00:16:20 -04:00
|
|
|
}
|
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
bool Object::ReplaceWithFile(FileBase *f)
|
2022-07-11 17:19:36 -04:00
|
|
|
{
|
2022-07-18 03:27:00 -04:00
|
|
|
data_.clear();
|
|
|
|
|
|
|
|
switch (this->filetype()) {
|
|
|
|
case MxOb::WAV:
|
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
if (f->ReadU32() != RIFF::RIFF_) {
|
2022-07-18 03:27:00 -04:00
|
|
|
return false;
|
2022-07-11 17:19:36 -04:00
|
|
|
}
|
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
// Skip total size
|
2022-07-18 14:25:00 -04:00
|
|
|
f->ReadU32();
|
2022-07-11 17:19:36 -04:00
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
if (f->ReadU32() != RIFF::WAVE) {
|
2022-07-18 03:27:00 -04:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-11 11:04:47 -04:00
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
bytearray fmt;
|
|
|
|
bytearray data;
|
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
while (!f->atEnd()) {
|
|
|
|
uint32_t id = f->ReadU32();
|
|
|
|
uint32_t sz = f->ReadU32();
|
2022-07-18 03:27:00 -04:00
|
|
|
if (id == RIFF::fmt_) {
|
2022-07-18 14:25:00 -04:00
|
|
|
fmt = f->ReadBytes(sz);
|
2022-07-18 03:27:00 -04:00
|
|
|
} else if (id == RIFF::data) {
|
2022-07-18 14:25:00 -04:00
|
|
|
data = f->ReadBytes(sz);
|
2022-07-18 03:27:00 -04:00
|
|
|
} else {
|
2022-07-18 14:25:00 -04:00
|
|
|
f->seek(sz, File::SeekCurrent);
|
2022-07-18 03:27:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmt.empty() || data.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_.push_back(fmt);
|
|
|
|
WAVFmt *fmt_info = fmt.cast<WAVFmt>();
|
|
|
|
size_t second_in_bytes = fmt_info->Channels * fmt_info->SampleRate * (fmt_info->BitsPerSample/8);
|
|
|
|
size_t max;
|
|
|
|
for (size_t i=0; i<data.size(); i+=max) {
|
|
|
|
max = std::min(data.size() - i, second_in_bytes);
|
|
|
|
data_.push_back(bytearray(data.data() + i, max));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case MxOb::SMK:
|
|
|
|
{
|
|
|
|
// Read header
|
2022-07-18 14:25:00 -04:00
|
|
|
bytearray hdr = f->ReadBytes(sizeof(SMK2));
|
2022-07-18 03:27:00 -04:00
|
|
|
|
|
|
|
// Read frame sizes
|
|
|
|
SMK2 smk = *hdr.cast<SMK2>();
|
2022-07-18 14:25:00 -04:00
|
|
|
bytearray frame_sizes = f->ReadBytes(smk.Frames * sizeof(uint32_t));
|
2022-07-18 03:27:00 -04:00
|
|
|
hdr.append(frame_sizes);
|
|
|
|
|
|
|
|
// Read frame types
|
2022-07-18 14:25:00 -04:00
|
|
|
hdr.append(f->ReadBytes(smk.Frames));
|
2022-07-18 03:27:00 -04:00
|
|
|
|
|
|
|
// Read Huffman trees
|
2022-07-18 14:25:00 -04:00
|
|
|
hdr.append(f->ReadBytes(smk.TreesSize));
|
2022-07-18 03:27:00 -04:00
|
|
|
|
|
|
|
// Place header into data vector
|
|
|
|
data_.resize(smk.Frames + 1);
|
|
|
|
data_[0] = hdr;
|
|
|
|
|
|
|
|
uint32_t *real_sizes = frame_sizes.cast<uint32_t>();
|
|
|
|
for (uint32_t i=0; i<smk.Frames; i++) {
|
|
|
|
uint32_t sz = real_sizes[i];
|
|
|
|
if (sz > 0) {
|
2022-07-18 14:25:00 -04:00
|
|
|
data_[i+1] = f->ReadBytes(sz);
|
2022-07-18 03:27:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2022-07-21 20:06:28 -04:00
|
|
|
case MxOb::STL:
|
|
|
|
{
|
|
|
|
BMP bmp;
|
|
|
|
f->ReadData(&bmp, sizeof(bmp));
|
|
|
|
|
|
|
|
bytearray info_header = f->ReadBytes(bmp.DataOffset - f->pos());
|
|
|
|
data_.push_back(info_header);
|
|
|
|
|
|
|
|
bytearray pixels = f->ReadBytes(bmp.FileSize - f->pos());
|
|
|
|
data_.push_back(pixels);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2022-07-30 11:25:28 -04:00
|
|
|
case MxOb::OBJ:
|
|
|
|
{
|
|
|
|
data_.push_back(f->ReadBytes(f->size()));
|
|
|
|
return true;
|
|
|
|
}
|
2022-07-18 03:27:00 -04:00
|
|
|
default:
|
|
|
|
LogWarning() << "Don't yet know how to chunk type " << RIFF::PrintU32AsString(this->filetype()) << std::endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2022-07-11 11:04:47 -04:00
|
|
|
}
|
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
bool Object::ExtractToFile(FileBase *f) const
|
2022-07-11 11:04:47 -04:00
|
|
|
{
|
2022-07-18 23:22:15 -04:00
|
|
|
if (data_.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
switch (this->filetype()) {
|
2022-07-11 04:48:20 -04:00
|
|
|
case MxOb::WAV:
|
|
|
|
{
|
2022-07-18 03:27:00 -04:00
|
|
|
// Write RIFF header
|
2022-07-18 14:25:00 -04:00
|
|
|
RIFF::Chk riff = RIFF::BeginChunk(f, RIFF::RIFF_);
|
2022-07-18 03:27:00 -04:00
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
f->WriteU32(RIFF::WAVE);
|
2022-07-18 03:27:00 -04:00
|
|
|
|
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
RIFF::Chk fmt = RIFF::BeginChunk(f, RIFF::fmt_);
|
2022-07-18 03:27:00 -04:00
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
f->WriteBytes(data_.at(0));
|
2022-07-18 03:27:00 -04:00
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
RIFF::EndChunk(f, fmt);
|
2022-07-18 03:27:00 -04:00
|
|
|
}
|
2022-07-11 04:48:20 -04:00
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
RIFF::Chk data = RIFF::BeginChunk(f, RIFF::data);
|
2022-07-18 03:27:00 -04:00
|
|
|
// Merge all chunks after the first one
|
|
|
|
for (size_t i=1; i<data_.size(); i++) {
|
2022-07-18 14:25:00 -04:00
|
|
|
f->WriteBytes(data_.at(i));
|
2022-07-18 03:27:00 -04:00
|
|
|
}
|
2022-07-18 14:25:00 -04:00
|
|
|
RIFF::EndChunk(f, data);
|
2022-07-11 04:48:20 -04:00
|
|
|
}
|
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
RIFF::EndChunk(f, riff);
|
2022-07-11 04:48:20 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MxOb::STL:
|
|
|
|
{
|
2022-07-21 20:06:28 -04:00
|
|
|
uint32_t size = sizeof(BMP);
|
|
|
|
for (size_t i=0; i<data_.size(); i++) {
|
|
|
|
size += data_.at(i).size();
|
|
|
|
}
|
2022-07-11 04:48:20 -04:00
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
// Write BMP header
|
2022-07-21 20:06:28 -04:00
|
|
|
BMP bmp;
|
|
|
|
bmp.Signature = 0x4D42; // 'BM'
|
|
|
|
bmp.FileSize = size;
|
|
|
|
bmp.Reserved = 0;
|
|
|
|
bmp.DataOffset = data_.at(0).size() + sizeof(BMP);
|
2022-07-11 04:48:20 -04:00
|
|
|
|
2022-07-21 20:06:28 -04:00
|
|
|
f->WriteData(&bmp, sizeof(bmp));
|
2022-07-18 03:27:00 -04:00
|
|
|
|
|
|
|
for (size_t i=0; i<data_.size(); i++) {
|
2022-07-18 14:25:00 -04:00
|
|
|
f->WriteBytes(data_.at(i));
|
2022-07-18 03:27:00 -04:00
|
|
|
}
|
2022-07-11 04:48:20 -04:00
|
|
|
break;
|
|
|
|
}
|
2022-07-11 17:20:56 -04:00
|
|
|
case MxOb::FLC:
|
|
|
|
{
|
|
|
|
// First chunk is a complete FLIC header, so add it as-is
|
2022-07-18 14:25:00 -04:00
|
|
|
f->WriteBytes(data_.at(0));
|
2022-07-11 17:20:56 -04:00
|
|
|
|
|
|
|
// Subsequent chunks are FLIC frames with an additional 20 byte header that needs to be stripped
|
|
|
|
const int CUSTOM_HEADER_SZ = 20;
|
2022-07-18 03:27:00 -04:00
|
|
|
for (size_t i=1; i<data_.size(); i++) {
|
2022-08-04 19:32:18 -04:00
|
|
|
if (data_.at(i).size() == CUSTOM_HEADER_SZ) {
|
|
|
|
static const char *empty_hdr = "\x10\x00\x00\x00\xfa\xf1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
|
|
|
f->WriteData(empty_hdr, 16);
|
|
|
|
} else {
|
|
|
|
f->WriteData(data_.at(i).data() + CUSTOM_HEADER_SZ, data_.at(i).size() - CUSTOM_HEADER_SZ);
|
|
|
|
}
|
2022-07-11 17:20:56 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-07-18 03:27:00 -04:00
|
|
|
default:
|
|
|
|
LogWarning() << "Didn't know how to extract type '" << RIFF::PrintU32AsString(filetype()) << "', merging..." << std::endl;
|
|
|
|
/* fall-through */
|
2022-07-17 19:25:06 -04:00
|
|
|
case MxOb::SMK:
|
|
|
|
case MxOb::OBJ:
|
|
|
|
// Simply merge
|
2022-07-18 03:27:00 -04:00
|
|
|
for (size_t i=0; i<data_.size(); i++) {
|
2022-07-18 14:25:00 -04:00
|
|
|
f->WriteBytes(data_.at(i));
|
2022-07-11 17:20:56 -04:00
|
|
|
}
|
|
|
|
break;
|
2022-07-11 04:48:20 -04:00
|
|
|
}
|
2022-07-11 11:04:47 -04:00
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
return true;
|
2022-07-11 11:04:47 -04:00
|
|
|
}
|
|
|
|
|
2022-07-18 03:27:00 -04:00
|
|
|
bytearray Object::ExtractToMemory() const
|
2022-07-11 11:04:47 -04:00
|
|
|
{
|
2022-07-18 14:25:00 -04:00
|
|
|
MemoryBuffer buf;
|
2022-07-18 03:27:00 -04:00
|
|
|
|
2022-07-18 14:25:00 -04:00
|
|
|
ExtractToFile(&buf);
|
2022-07-18 03:27:00 -04:00
|
|
|
|
|
|
|
return buf.data();
|
2022-07-11 11:04:47 -04:00
|
|
|
}
|
|
|
|
|
2022-07-17 19:25:06 -04:00
|
|
|
const bytearray &Object::GetFileHeader() const
|
2022-07-11 11:04:47 -04:00
|
|
|
{
|
2022-07-17 19:25:06 -04:00
|
|
|
return data_.at(0);
|
2022-07-11 11:04:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bytearray Object::GetFileBody() const
|
|
|
|
{
|
|
|
|
bytearray b;
|
|
|
|
|
2022-07-17 19:25:06 -04:00
|
|
|
for (size_t i=1; i<data_.size(); i++) {
|
|
|
|
b.append(data_.at(i));
|
2022-07-11 11:04:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Object::GetFileBodySize() const
|
|
|
|
{
|
|
|
|
size_t s = 0;
|
2022-07-17 19:25:06 -04:00
|
|
|
|
|
|
|
for (size_t i=1; i<data_.size(); i++) {
|
|
|
|
s += data_.at(i).size();
|
2022-07-11 11:04:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
2022-07-11 04:48:20 -04:00
|
|
|
}
|
|
|
|
|
2023-08-21 21:12:38 -04:00
|
|
|
size_t Object::CalculateMaximumDiskSize() const
|
|
|
|
{
|
|
|
|
size_t s = 0;
|
|
|
|
|
|
|
|
s += 108;
|
|
|
|
s += presenter_.size() + 1;
|
|
|
|
s += name_.size() + 1;
|
|
|
|
s += extra_.size();
|
|
|
|
|
|
|
|
if (type_ != MxOb::Presenter && type_ != MxOb::World && type_ != MxOb::Animation) {
|
|
|
|
s += filename_.size() + 1;
|
|
|
|
s += 24;
|
|
|
|
|
|
|
|
if (filetype_ == MxOb::WAV) {
|
|
|
|
s += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->HasChildren()) {
|
|
|
|
s += 16;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < this->GetChildCount(); i++) {
|
|
|
|
s += static_cast<Object*>(this->GetChildAt(i))->CalculateMaximumDiskSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2022-07-11 04:48:20 -04:00
|
|
|
Object *Object::FindSubObjectWithID(uint32_t id)
|
|
|
|
{
|
|
|
|
if (this->id() == id) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2022-07-17 19:25:06 -04:00
|
|
|
for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) {
|
|
|
|
if (Object *o = static_cast<Object*>(*it)->FindSubObjectWithID(id)) {
|
2022-07-11 04:48:20 -04:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-07-11 00:16:20 -04:00
|
|
|
}
|