diff --git a/CMakeLists.txt b/CMakeLists.txt index 8119c5b..685a151 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,14 +144,14 @@ set(EHS_SOURCES src/io/img/PNG_Chunk.cpp include/ehs/io/img/PNG_Chunk.h src/io/img/ImgCodec.cpp include/ehs/io/img/ImgCodec.h - include/ehs/io/model/Vertex.h - src/io/model/Mesh.cpp include/ehs/io/model/Mesh.h - src/io/model/Bone.cpp include/ehs/io/model/Bone.h - src/io/model/Model.cpp include/ehs/io/model/Model.h - src/io/model/Animation.cpp include/ehs/io/model/Animation.h - src/io/model/AnimBone.cpp include/ehs/io/model/AnimBone.h - src/io/model/KeyFrame.cpp include/ehs/io/model/KeyFrame.h - src/io/model/PropertyChange.cpp include/ehs/io/model/PropertyChange.h + include/ehs/io/mdl/Vertex.h + src/io/model/Mesh.cpp include/ehs/io/mdl/Mesh.h + src/io/model/Bone.cpp include/ehs/io/mdl/Bone.h + src/io/model/Mdl.cpp include/ehs/io/mdl/Mdl.h + src/io/model/Animation.cpp include/ehs/io/mdl/Animation.h + src/io/model/AnimBone.cpp include/ehs/io/mdl/AnimBone.h + src/io/model/KeyFrame.cpp include/ehs/io/mdl/KeyFrame.h + src/io/model/PropertyChange.cpp include/ehs/io/mdl/PropertyChange.h src/io/hid/ButtonState.cpp include/ehs/io/hid/ButtonState.h src/io/hid/Button.cpp include/ehs/io/hid/Button.h @@ -160,6 +160,8 @@ set(EHS_SOURCES src/io/hid/HID.cpp include/ehs/io/hid/HID.h src/io/hid/InputHandler.cpp include/ehs/io/hid/InputHandler.h src/io/hid/Input.cpp include/ehs/io/hid/Input.h + src/io/model/MdlCodec.cpp + include/ehs/io/mdl/MdlCodec.h ) if (IS_OS_WINDOWS) diff --git a/include/ehs/io/audio/Audio.h b/include/ehs/io/audio/Audio.h index 556d7b1..4136da5 100644 --- a/include/ehs/io/audio/Audio.h +++ b/include/ehs/io/audio/Audio.h @@ -39,7 +39,7 @@ namespace ehs Audio(); - Audio(Str_8 id); + Audio(const Str_8& filePath); Audio(Str_8 id, UInt_64 sampleRate, DataType dataType, UInt_8 channels, UInt_64 frames, const Byte* data); @@ -123,17 +123,7 @@ namespace ehs Audio GetAsChannels(UInt_8 newChannels) const; - bool ToFile(const Str_8& filePath) const; - - static Audio FromFile(const Str_8& filePath); - - static Audio* FromFile_Heap(const Str_8& filePath); - - static Audio FromFile(const Str_8& filePath, DataType required); - - static Audio* FromFile_Heap(const Str_8& filePath, DataType required); - - static Audio FromData(Str_8 id, const Str_8& ext, Serializer& data); + bool Export(const Str_8& filePath) const; private: void ToMono(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; @@ -201,4 +191,10 @@ namespace ehs void SInt_32_to_SInt_64(Byte* newData, Byte* newPeak) const; }; + + bool EncodeEHA(const ehs::AudioCodec* codec, ehs::Serializer& out, const ehs::Audio* in); + + bool DecodeEHA(const ehs::AudioCodec* codec, ehs::Serializer& in, ehs::Audio* out); + + bool DecodeWAV(const ehs::AudioCodec* codec, ehs::Serializer& in, ehs::Audio* out); } \ No newline at end of file diff --git a/include/ehs/io/img/Img.h b/include/ehs/io/img/Img.h index 05b2759..672bd4e 100644 --- a/include/ehs/io/img/Img.h +++ b/include/ehs/io/img/Img.h @@ -42,7 +42,7 @@ namespace ehs Img(); - Img(Str_8 id); + Img(const Str_8& filePath); Img(Str_8 id, UInt_8 byteDepth, UInt_8 channels, const Vec2_u64& resolution, const Byte* data); @@ -124,13 +124,7 @@ namespace ehs bool IsValid() const; - bool ToFile(const Str_8& filePath) const; - - static Img FromFile(const Str_8& filePath); - - static Img* FromFile_Heap(const Str_8& filePath); - - static Img FromData(Str_8 id, const Str_8& ext, Serializer& data); + bool Export(const Str_8& filePath) const; private: Img GetNearestNeighbor(const Vec2_u64& newResolution) const; @@ -185,4 +179,10 @@ namespace ehs void BD16_to_BD8(UInt_64 newSize, Byte* buffer) const; }; + + bool EncodeQOI(const ehs::ImgCodec* codec, ehs::Serializer& out, const ehs::Img* in); + + bool DecodeQOI(const ehs::ImgCodec* codec, ehs::Serializer& in, ehs::Img* out); + + bool DecodePNG(const ehs::ImgCodec* codec, ehs::Serializer& in, ehs::Img* out); } diff --git a/include/ehs/io/img/ImgCodec.h b/include/ehs/io/img/ImgCodec.h index deb44b2..0ab00a6 100644 --- a/include/ehs/io/img/ImgCodec.h +++ b/include/ehs/io/img/ImgCodec.h @@ -7,6 +7,10 @@ namespace ehs { class Img; + class ImgCodec; + + typedef bool (*EncodeImgCb)(const ImgCodec* const, Serializer&, const Img*); + typedef bool (*DecodeImgCb)(const ImgCodec* const, Serializer&, Img*); class ImgCodec { @@ -15,15 +19,13 @@ namespace ehs UInt_64 hashExt; Str_8 ext; Endianness endianness; - bool (*encodeCb)(const ImgCodec* const, Serializer&, const Img*); - bool (*decodeCb)(const ImgCodec* const, Serializer&, Img*); + EncodeImgCb encoder; + DecodeImgCb decoder; public: ImgCodec(); - ImgCodec(Str_8 id, Str_8 ext, const Endianness end, - bool (*encodeCb)(const ImgCodec* const, Serializer&, const Img*), - bool (*decodeCb)(const ImgCodec* const, Serializer&, Img*)); + ImgCodec(Str_8 id, Str_8 ext, Endianness end, EncodeImgCb encoder, DecodeImgCb decoder); ImgCodec(ImgCodec&& codec) noexcept; diff --git a/include/ehs/io/model/AnimBone.h b/include/ehs/io/mdl/AnimBone.h similarity index 100% rename from include/ehs/io/model/AnimBone.h rename to include/ehs/io/mdl/AnimBone.h diff --git a/include/ehs/io/model/Animation.h b/include/ehs/io/mdl/Animation.h similarity index 100% rename from include/ehs/io/model/Animation.h rename to include/ehs/io/mdl/Animation.h diff --git a/include/ehs/io/model/Bone.h b/include/ehs/io/mdl/Bone.h similarity index 100% rename from include/ehs/io/model/Bone.h rename to include/ehs/io/mdl/Bone.h diff --git a/include/ehs/io/model/KeyFrame.h b/include/ehs/io/mdl/KeyFrame.h similarity index 100% rename from include/ehs/io/model/KeyFrame.h rename to include/ehs/io/mdl/KeyFrame.h diff --git a/include/ehs/io/mdl/Mdl.h b/include/ehs/io/mdl/Mdl.h new file mode 100644 index 0000000..4fc3f39 --- /dev/null +++ b/include/ehs/io/mdl/Mdl.h @@ -0,0 +1,93 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Array.h" +#include "ehs/io/File.h" +#include "MdlCodec.h" +#include "Mesh.h" +#include "Bone.h" +#include "Animation.h" + +namespace ehs +{ + enum class ModelEncoding : UInt_8 + { + EHM + }; + + class Mdl : public BaseObj + { + private: + static Array codecs; + + protected: + UInt_64 hashId; + Str_8 id; + Array meshes; + Bone skeleton; + Array animations; + + public: + static bool HasCodec(UInt_64 hashExt); + + static bool HasCodec(const Str_8& ext); + + static bool AddCodec(MdlCodec codec); + + static const MdlCodec* GetCodec(UInt_64 hashExt); + + static const MdlCodec* GetCodec(const Str_8& ext); + + Mdl(); + + Mdl(const Str_8& filePath); + + Mdl(Str_8 id, Array meshes, Bone skeleton, Array animations); + + Mdl(Str_8 id, Array meshes, Bone skeleton); + + Mdl(Str_8 id, Array meshes); + + Mdl(Mdl&& model) noexcept; + + Mdl(const Mdl& model) = default; + + Mdl& operator=(Mdl&& model) noexcept; + + Mdl& operator=(const Mdl& model) = default; + + void Release(); + + UInt_64 GetHashId() const; + + void SetId(Str_8 newId); + + Str_8 GetId() const; + + const Array& GetMeshes() const; + + Array& GetMeshes(); + + Mesh* GetMesh(UInt_64 inHashId); + + Mesh* GetMesh(const Str_8& inId); + + const Bone& GetSkeleton() const; + + Bone& GetSkeleton(); + + Animation* GetAnimation(UInt_64 inHashId); + + const Array& GetAnimations() const; + + Array& GetAnimations(); + + void Calculate(); + + bool Export(const Str_8& filePath) const; + }; + + bool EncodeEHM(const MdlCodec* codec, Serializer& data, const Mdl* mdl); + + bool DecodeEHM(const MdlCodec* codec, Serializer& data, Mdl* mdl); +} diff --git a/include/ehs/io/mdl/MdlCodec.h b/include/ehs/io/mdl/MdlCodec.h new file mode 100644 index 0000000..a0a3d65 --- /dev/null +++ b/include/ehs/io/mdl/MdlCodec.h @@ -0,0 +1,50 @@ +#pragma once + +#include "ehs/Str.h" +#include "ehs/Serializer.h" +#include "ehs/system/CPU.h" + +namespace ehs +{ + class Mdl; + class MdlCodec; + + typedef bool (*EnocdeMdlCb)(const MdlCodec*, Serializer&, const Mdl*); + typedef bool (*DecodeMdlCb)(const MdlCodec*, Serializer&, Mdl*); + + class MdlCodec + { + private: + Str_8 id; + UInt_64 hashExt; + Str_8 ext; + Endianness endianness; + EnocdeMdlCb encoder; + DecodeMdlCb decoder; + + public: + MdlCodec(); + + MdlCodec(Str_8 id, Str_8 ext, Endianness end, EnocdeMdlCb encoder, DecodeMdlCb decoder); + + MdlCodec(MdlCodec&& codec) noexcept; + + MdlCodec(const MdlCodec& codec); + + MdlCodec& operator=(MdlCodec&& codec) noexcept; + + MdlCodec& operator=(const MdlCodec& codec); + + Str_8 GetId() const; + + UInt_64 GetHashExt() const; + + Str_8 GetExt() const; + + Endianness GetEndianness() const; + + bool Encode(Serializer& out, const Mdl* in) const; + + bool Decode(Serializer& in, Mdl* out) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/model/Mesh.h b/include/ehs/io/mdl/Mesh.h similarity index 100% rename from include/ehs/io/model/Mesh.h rename to include/ehs/io/mdl/Mesh.h diff --git a/include/ehs/io/model/PropertyChange.h b/include/ehs/io/mdl/PropertyChange.h similarity index 100% rename from include/ehs/io/model/PropertyChange.h rename to include/ehs/io/mdl/PropertyChange.h diff --git a/include/ehs/io/model/Vertex.h b/include/ehs/io/mdl/Vertex.h similarity index 100% rename from include/ehs/io/model/Vertex.h rename to include/ehs/io/mdl/Vertex.h diff --git a/include/ehs/io/model/Model.h b/include/ehs/io/model/Model.h deleted file mode 100644 index 1bc3488..0000000 --- a/include/ehs/io/model/Model.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include "ehs/EHS.h" -#include "ehs/Array.h" -#include "ehs/io/File.h" -#include "Mesh.h" -#include "Bone.h" -#include "Animation.h" - -namespace ehs -{ - enum class ModelEncoding : UInt_8 - { - EHM - }; - - class Model : public BaseObj - { - protected: - UInt_64 hashId; - Str_8 id; - Array meshes; - Bone skeleton; - Array animations; - - public: - Model(); - - Model(const Str_8& filePath); - - Model(Str_8 id, Array meshes, Bone skeleton, Array animations); - - Model(Str_8 id, Array meshes, Bone skeleton); - - Model(Str_8 id, Array meshes); - - Model(Model&& model) noexcept; - - Model(const Model& model) = default; - - Model& operator=(Model&& model) noexcept; - - Model& operator=(const Model& model) = default; - - void Release(); - - UInt_64 GetHashId() const; - - void SetId(Str_8 newId); - - Str_8 GetId() const; - - const Array& GetMeshes() const; - - Array& GetMeshes(); - - Mesh* GetMesh(UInt_64 inHashId); - - Mesh* GetMesh(const Str_8& inId); - - const Bone& GetSkeleton() const; - - Bone& GetSkeleton(); - - Animation* GetAnimation(UInt_64 inHashId); - - const Array& GetAnimations() const; - - Array& GetAnimations(); - - void Calculate(); - - void Export(const Str_8& filePath, ModelEncoding encoding); - - private: - void ToEHM(File& file); - - void FromEHM(File& file); - }; -} diff --git a/src/EHS.cpp b/src/EHS.cpp index 779cd9d..5223dca 100644 --- a/src/EHS.cpp +++ b/src/EHS.cpp @@ -7,6 +7,7 @@ #include "ehs/io/img/Img.h" #include "ehs/io/img/PNG.h" #include "ehs/io/RIFF.h" +#include "ehs/io/mdl/Mdl.h" #include @@ -94,531 +95,6 @@ namespace ehs { return appVer; } - - bool DecodeWAV(const ehs::AudioCodec* const codec, ehs::Serializer& in, ehs::Audio* out) - { - RIFF riff(in); - - if (riff.GetType() != "WAVE") - { - EHS_LOG_INT("Error", 0, "Data is not in WAVE format."); - return false; - } - - RIFF_Chunk fmt = riff.GetChunk("fmt "); - if (!fmt.IsValid()) - { - EHS_LOG_INT("Error", 1, "Wave does not have a format chunk."); - return false; - } - - Serializer<> fmtSer = fmt.GetData(); - - RIFF_Chunk dChunk = riff.GetChunk("data"); - if (!dChunk.IsValid()) - { - EHS_LOG_INT("Error", 2, "Wave does not have a data chunk."); - return false; - } - - UInt_16 compression = fmtSer.Read(); - if (compression == 0x2) - { - EHS_LOG_INT("Error", 3, "Microsoft ADPCM compression unsupported."); - return false; - } - else if (compression == 0x6) - { - EHS_LOG_INT("Error", 4, "ITU G.711 a-law compression unsupported."); - return false; - } - else if (compression == 0x7) - { - EHS_LOG_INT("Error", 5, "ITU G.711 µ-law compression unsupported."); - return false; - } - else if (compression == 0x11) - { - EHS_LOG_INT("Error", 6, "IMA ADPCM compression unsupported."); - return false; - } - else if (compression == 0x16) - { - EHS_LOG_INT("Error", 7, "TU G.723 ADPCM (Yamaha) compression unsupported."); - return false; - } - else if (compression == 0x31) - { - EHS_LOG_INT("Error", 8, "GSM 6.10 compression unsupported."); - return false; - } - else if (compression == 0x40) - { - EHS_LOG_INT("Error", 9, "ITU G.721 ADPCM compression unsupported."); - return false; - } - else if (compression == 0x50) - { - EHS_LOG_INT("Error", 10, "MPEG compression unsupported."); - return false; - } - else if (compression == 0xFFFF) - { - EHS_LOG_INT("Error", 11, "Experimental compression unsupported."); - return false; - } - else if (compression != 0x1 && compression != 0x3) - { - EHS_LOG_INT("Error", 12, "Wave has unknown compression of " + Str_8::FromNum(compression) + "."); - return false; - } - - UInt_16 channels = fmtSer.Read(); - UInt_32 sampleRate = fmtSer.Read(); - fmtSer.SetOffset(fmtSer.GetOffset() + 6); - UInt_8 byteDepth = (UInt_8)(fmtSer.Read() / 8); - - DataType dataType; - if (byteDepth == 1) - dataType = DataType::SINT_8; - else if (byteDepth == 2) - dataType = DataType::SINT_16; - else if (byteDepth == 3) - dataType = DataType::SINT_24; - else if (byteDepth == 4 && compression == 0x3) - dataType = DataType::FLOAT; - else if (byteDepth == 4) - dataType = DataType::SINT_32; - else if (byteDepth == 8) - dataType = DataType::SINT_64; - else - return false; - - UInt_64 size = dChunk.GetData().Size(); - UInt_64 frames = size / byteDepth / channels; - - *out = std::move(Audio(out->GetId(), sampleRate, dataType, channels, frames)); - - Serializer<> dataSer = dChunk.GetData(); - - for (UInt_32 i = 0; i < dataSer.Size(); i += byteDepth) - { - if (byteDepth == 1) - { - *(SInt_8*)&(*out)[i] = dataSer.Read(); - if ((*out)[i] > *(SInt_8*)out->GetPeak()) - out->SetPeak(sizeof(SInt_8), &(*out)[i]); - } - else if (byteDepth == 2) - { - *(SInt_16*)&(*out)[i] = dataSer.Read(); - if (*(SInt_16*)&(*out)[i] > *(SInt_16*)out->GetPeak()) - out->SetPeak(sizeof(SInt_16), &(*out)[i]); - } - else if (byteDepth == 3) - { - *(SInt_16*)&(*out)[i + 1] = dataSer.Read(); - (*out)[i] = dataSer.Read(); - - SInt_32 signal = 0; - signal |= (*out)[i]; - signal |= (*out)[i + 1] << 8; - signal |= (*out)[i + 2] << 16; - - SInt_32 peak = 0; - peak |= out->GetPeak()[0]; - peak |= out->GetPeak()[1] << 8; - peak |= out->GetPeak()[2] << 16; - - if (signal > peak) - out->SetPeak(3, &(*out)[i]); - } - else if (byteDepth == 4 && compression == 0x3) - { - *(float*)&(*out)[i] = dataSer.Read(); - if (*(float*)&(*out)[i] > *(float*)out->GetPeak()) - out->SetPeak(sizeof(float), &(*out)[i]); - } - else if (byteDepth == 4) - { - *(SInt_32*)&(*out)[i] = dataSer.Read(); - if (*(SInt_32*)&(*out)[i] > *(SInt_32*)out->GetPeak()) - out->SetPeak(sizeof(SInt_32), &(*out)[i]); - } - else if (byteDepth == 8) - { - *(SInt_64*)&(*out)[i] = dataSer.Read(); - if (*(SInt_64*)&(*out)[i] > *(SInt_64*)out->GetPeak()) - out->SetPeak(sizeof(SInt_64), &(*out)[i]); - } - } - - return true; - } - - bool EncodeEHA(const ehs::AudioCodec* const codec, ehs::Serializer& out, const ehs::Audio* in) - { - Serializer result(codec->GetEndianness()); - result.WriteVersion({1, 0, 0}); - result.Write(in->GetSampleRate()); - result.Write(in->GetDataType()); - result.Write(in->GetByteDepth()); - result.Write(in->GetChannels()); - result.Write(in->GetFrameCount()); - - UInt_64 size = in->GetSize(); - UInt_8 byteDepth = in->GetByteDepth(); - - result.Resize(result.Size() + size + byteDepth); - Util::Copy(&result[result.GetOffset()], &in[0], size); - result.SetOffset(result.GetOffset() + size); - - Util::Copy(&result[result.GetOffset()], in->GetPeak(), byteDepth); - - return true; - } - - bool DecodeEHA(const ehs::AudioCodec* const codec, ehs::Serializer& in, ehs::Audio* out) - { - Version version = in.ReadVersion(); - if (version != Version(1, 0, 0)) - { - EHS_LOG_INT("Error", 0, "Incompatible audio file version."); - return false; - } - - UInt_64 sampleRate = in.Read(); - DataType dataType = in.Read(); - UInt_8 byteDepth = in.Read(); - UInt_8 channels = in.Read(); - UInt_64 frames = in.Read(); - - *out = Audio(out->GetId(), sampleRate, dataType, channels, frames); - - UInt_64 size = out->GetSize(); - Util::Copy(&(*out)[0], &in[in.GetOffset()], size); - in.SetOffset(in.GetOffset() + size); - - out->SetPeak(byteDepth, &in[in.GetOffset()]); - - return true; - } - - bool DecodePNG(const ehs::ImgCodec* const codec, ehs::Serializer& in, ehs::Img* out) - { - PNG png(out->GetId(), in); - - PNG_Chunk* ihdr = png.GetChunk("IHDR"); - Serializer* ihdrData = ihdr->GetData(); - - UInt_32 width = ihdrData->Read(); - UInt_32 height = ihdrData->Read(); - UInt_8 bitDepth = ihdrData->Read(); - - UInt_8 colorType = ihdrData->Read(); - if (colorType == 3) - { - EHS_LOG_INT("Error", 1, "Color type of " + Str_8::FromNum(colorType) + " is unsupported."); - return false; - } - - UInt_8 channels = 1; - if (colorType == 2) - channels = 3; - else if (colorType == 4) - channels = 2; - else if (colorType == 6) - channels = 4; - - *out = Img(out->GetId(), bitDepth / 8, channels, {width, height}); - - UInt_8 compression = ihdrData->Read(); - if (compression) - { - EHS_LOG_INT("Error", 2, "Compression method of " + Str_8::FromNum(compression) + " is unsupported."); - return false; - } - - UInt_8 filter = ihdrData->Read(); - if (filter) - { - EHS_LOG_INT("Error", 3, "Filter method of " + Str_8::FromNum(filter) + " is unsupported."); - return false; - } - - UInt_8 interlaced = ihdrData->Read(); - if (interlaced) - { - EHS_LOG_INT("Error", 4, "Interlacing method of " + Str_8::FromNum(interlaced) + " is unsupported."); - return false; - } - - UInt_32 scanline = width * (bitDepth / 8) * channels; - UInt_32 scanLineF = scanline + 1; - UInt_32 bufferSize = scanline * height + height; - - Byte* buffer = new Byte[bufferSize]; - - PNG_Chunk* idat = png.GetChunk("IDAT"); - Serializer* idatData = idat->GetData(); - - z_stream strm = {}; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = idatData->Size(); - strm.next_in = *idatData; - strm.avail_out = bufferSize; - strm.next_out = buffer; - - int code = inflateInit(&strm); - if (code != Z_OK) - { - EHS_LOG_INT("Error", 5, "Failed to initialize zlib inflate with error #" + Str_8::FromNum(code) + "."); - delete[] buffer; - return false; - } - - do - { - code = inflate(&strm, Z_NO_FLUSH); - if (code != Z_STREAM_END && code != Z_OK) - { - EHS_LOG_INT("Error", 6, "Failed to zlib inflate with error #" + Str_8::FromNum(code) + "."); - delete[] buffer; - return false; - } - } while (strm.avail_out); - - code = inflateEnd(&strm); - if (code != Z_OK) - { - EHS_LOG_INT("Error", 7, "Failed to uninitialize zlib inflate with error #" + Str_8::FromNum(code) + "."); - delete[] buffer; - return false; - } - - for (UInt_32 i = 0, o = 0; i < bufferSize; i += scanLineF, o += scanline) - { - UInt_8 fCode = buffer[i]; - if (fCode == 0) - PNG::FilterNone(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline); - else if (fCode == 1) - PNG::FilterSub(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline); - else if (fCode == 2) - PNG::FilterUp(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); - else if (fCode == 3) - PNG::FilterAverage(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); - else if (fCode == 4) - PNG::FilterPaeth(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); - } - - delete[] buffer; - - return true; - } - - bool EncodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer& out, const ehs::Img* in) - { - UInt_8 channels = in->GetChannels(); - Vec2_u64 resolution = in->GetResolution(); - - UInt_32 px_len = resolution.x * resolution.y * channels; - UInt_32 px_end = px_len - channels; - - Byte index[256]; - for (UInt_64 i = 0; i < 64; ++i) - *(UInt_32*)&index[i * 4] = 0; - - Byte prevPixel[4] = {0, 0, 0, 255}; - Byte pixel[4] = {0, 0, 0, 255}; - - Serializer result(Endianness::BE, resolution.x * resolution.y * (channels + 1) + 22); - - result.Write('q'); - result.Write('o'); - result.Write('i'); - result.Write('f'); - result.Write(resolution.x); - result.Write(resolution.y); - result.Write(in->GetChannels()); - result.Write(1); - - for (UInt_32 px_pos = 0, run = 0; px_pos < px_len; px_pos += channels) - { - if (channels == 4) - { - *(UInt_32*)pixel = *(UInt_32*)&(*in)[px_pos]; - } - else - { - pixel[0] = (*in)[px_pos]; - pixel[1] = (*in)[px_pos + 1]; - pixel[2] = (*in)[px_pos + 2]; - } - - if (*(UInt_32*)pixel == *(UInt_32*)prevPixel) - { - run++; - if (run == 62 || px_pos == px_end) - { - result.Write(0xc0 | (run - 1)); - run = 0; - } - } - else - { - if (run > 0) - { - result.Write(0xc0 | (run - 1)); - run = 0; - } - - UInt_32 index_pos = (prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels; - - if (*(UInt_32*)&index[index_pos] == *(UInt_32*)pixel) - { - result.Write(0x00 | (index_pos / channels)); - } - else - { - *(UInt_32*)&index[index_pos] = *(UInt_32*)pixel; - - if (pixel[3] == prevPixel[3]) - { - SInt_8 vr = pixel[0] - prevPixel[0]; - SInt_8 vg = pixel[1] - prevPixel[1]; - SInt_8 vb = pixel[2] - prevPixel[2]; - - SInt_8 vg_r = vr - vg; - SInt_8 vg_b = vb - vg; - - if ( - vr > -3 && vr < 2 && - vg > -3 && vg < 2 && - vb > -3 && vb < 2 - ) - { - result.Write(0x40 | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2)); - } - else if ( - vg_r > -9 && vg_r < 8 && - vg > -33 && vg < 32 && - vg_b > -9 && vg_b < 8 - ) - { - result.Write(0x80 | (vg + 32)); - result.Write((vg_r + 8) << 4 | (vg_b + 8)); - } - else - { - result.Write(0xfe); - result.Write(pixel[0]); - result.Write(pixel[1]); - result.Write(pixel[2]); - } - } - else - { - result.Write(0xff); - result.SetEndianness(CPU::GetEndianness()); - result.Write(*(UInt_32*)pixel); - result.SetEndianness(Endianness::BE); - } - } - } - - *(UInt_32*)prevPixel = *(UInt_32*)pixel; - } - - result.Write(0x100000000000000); - - return true; - } - - bool DecodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer& in, ehs::Img* out) - { - Str_8 imgType = in.ReadStr(4); - if (imgType != "qoif") - { - EHS_LOG_INT("Error", 0, "Given data is not in the qoif format."); - return false; - } - - UInt_64 width = in.Read(); - UInt_64 height = in.Read(); - - UInt_8 channels = in.Read(); - channels = 4; - UInt_8 space = in.Read(); - UInt_8 bitDepth = 8; - - UInt_64 size = width * channels * height; - - *out = Img(out->GetId(), bitDepth / 8, channels, {width, height}); - - Byte prevPixel[4] = {0, 0, 0, 255}; - - Byte index[256]; - for (UInt_64 i = 0; i < 64; ++i) - *(UInt_32*)&index[i * 4] = 0; - - UInt_32 chunksLen = in.Size() - 8; - for (UInt_32 pos = 0, run = 0; pos < size; pos += channels) - { - if (run > 0) - --run; - else if (in.GetOffset() < chunksLen) - { - UInt_32 chunkType = (UInt_32)in.Read(); - if (chunkType == 0xfe) // RGB - { - prevPixel[0] = in.Read(); // R-value - prevPixel[1] = in.Read(); // G-value - prevPixel[2] = in.Read(); // B-value - } - else if (chunkType == 0xff) // RGBA - { - *(UInt_32*)prevPixel = in.Read(); - } - else if ((chunkType & 0xc0) == 0x00) // Index - { - *(UInt_32*)prevPixel = *(UInt_32*)&index[chunkType * channels]; - } - else if ((chunkType & 0xc0) == 0x40) // Diff - { - prevPixel[0] += ((chunkType >> 4) & 0x03) - 2; // R-value - prevPixel[1] += ((chunkType >> 2) & 0x03) - 2; // G-value - prevPixel[2] += (chunkType & 0x03) - 2; // B-value - } - else if ((chunkType & 0xc0) == 0x80) // Luma - { - UInt_32 mod = (UInt_32)in.Read(); - UInt_32 vg = (chunkType & 0x3f) - 32; - prevPixel[0] += vg - 8 + ((mod >> 4) & 0x0f); // R-value - prevPixel[1] += vg; // G-value - prevPixel[2] += vg - 8 + (mod & 0x0f); // B-value - } - else if ((chunkType & 0xc0) == 0xc0) // Run - run = (chunkType & 0x3f); - - *(UInt_32*)&index[(prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels] = *(UInt_32*)prevPixel; - } - - if (channels == 4) - { - *((UInt_32*)&(*out)[pos]) = *(UInt_32*)prevPixel; - } - else - { - (*out)[pos] = prevPixel[0]; - (*out)[pos + 1] = prevPixel[1]; - (*out)[pos + 2] = prevPixel[2]; - } - } - - return true; - } } void LogRaised(const ehs::Log& log) @@ -677,6 +153,14 @@ int main() ehs::DecodeQOI }); + ehs::Mdl::AddCodec({ + "Event Horizon Model", + "ehm", + ehs::Endianness::LE, + ehs::EncodeEHM, + ehs::DecodeEHM + }); + ehs::GarbageCollector::Start(); const ehs::SInt_32 code = Main(&ehs::appName, &ehs::appVerId, &ehs::appVer); diff --git a/src/io/audio/Audio.cpp b/src/io/audio/Audio.cpp index b743a3c..4daf7cb 100644 --- a/src/io/audio/Audio.cpp +++ b/src/io/audio/Audio.cpp @@ -57,6 +57,29 @@ namespace ehs AddType("Audio"); } + Audio::Audio(const Str_8& filePath) + : Resource(File::ParseName_8(filePath)), sampleRate(0), dataType(DataType::FLOAT), byteDepth(0), channels(0), + frames(0), length(0.0f), data(nullptr), peak(nullptr) + { + AddType("Audio"); + + File file(filePath, Mode::READ, Disposition::OPEN); + Str_8 ext = file.GetExtension(); + + const AudioCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return; + } + + Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); + + file.Release(); + + codec->Decode(data, this); + } + Audio::Audio(Str_8 id, const UInt_64 sampleRate, const DataType dataType, const UInt_8 channels, const UInt_64 frames, const Byte* const data) : Resource((Str_8&&)id), dataType(dataType), byteDepth(ToByteDepth(dataType)), sampleRate(sampleRate), channels(channels), frames(frames), length((float)frames / (float)sampleRate), @@ -105,13 +128,6 @@ namespace ehs AddType("Audio"); } - Audio::Audio(Str_8 id) - : Resource((Str_8&&)id), sampleRate(0), dataType(DataType::FLOAT), byteDepth(0), channels(0), - frames(0), length(0.0f), data(nullptr), peak(nullptr) - { - AddType("Audio"); - } - Audio::Audio(Audio&& audio) noexcept : Resource((Resource&&)audio), sampleRate(audio.sampleRate), dataType(audio.dataType), byteDepth(audio.byteDepth), channels(audio.channels), frames(audio.frames), length(audio.length), @@ -853,7 +869,7 @@ namespace ehs return result; } - bool Audio::ToFile(const Str_8& filePath) const + bool Audio::Export(const Str_8& filePath) const { Str_8 ext = File::ParseExt_8(filePath); @@ -874,96 +890,6 @@ namespace ehs return true; } - Audio Audio::FromFile(const Str_8& filePath) - { - File file(filePath, Mode::READ, Disposition::OPEN); - Str_8 ext = file.GetExtension(); - - Audio result(file.GetName()); - - const AudioCodec* codec = GetCodec(ext); - if (!codec) - { - EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); - return result; - } - - Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); - - file.Release(); - - if (!codec->Decode(data, &result)) - return {}; - - return result; - } - - Audio* Audio::FromFile_Heap(const Str_8& filePath) - { - File file(filePath, Mode::READ, Disposition::OPEN); - Str_8 ext = file.GetExtension(); - - Audio* result = nullptr; - - const AudioCodec* codec = GetCodec(ext); - if (!codec) - { - EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); - return result; - } - - result = new Audio(file.GetName()); - - Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); - - file.Release(); - - if (!codec->Decode(data, result)) - { - delete result; - return nullptr; - } - - return result; - } - - Audio Audio::FromFile(const Str_8& filePath, const DataType required) - { - Audio result = std::move(FromFile(filePath)); - if (!result.data) - return result; - - result.ToDataType(required); - return result; - } - - Audio* Audio::FromFile_Heap(const Str_8& filePath, const DataType required) - { - Audio* result = FromFile_Heap(filePath); - if (!result) - return result; - - result->ToDataType(required); - return result; - } - - Audio Audio::FromData(Str_8 id, const Str_8& ext, Serializer& data) - { - Audio result((Str_8&&)id); - - const AudioCodec* codec = GetCodec(ext); - if (!codec) - { - EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); - return result; - } - - if (!codec->Decode(data, &result)) - return {}; - - return result; - } - void Audio::ToMono(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const { switch (dataType) @@ -1948,4 +1874,213 @@ namespace ehs *(SInt_64*)newPeak = (SInt_64)((float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX * (float)EHS_SINT_64_MAX); } + + bool EncodeEHA(const ehs::AudioCodec* const codec, ehs::Serializer& out, const ehs::Audio* const in) + { + Serializer result(codec->GetEndianness()); + result.WriteVersion({1, 0, 0}); + result.Write(in->GetSampleRate()); + result.Write(in->GetDataType()); + result.Write(in->GetByteDepth()); + result.Write(in->GetChannels()); + result.Write(in->GetFrameCount()); + + UInt_64 size = in->GetSize(); + UInt_8 byteDepth = in->GetByteDepth(); + + result.Resize(result.Size() + size + byteDepth); + Util::Copy(&result[result.GetOffset()], &in[0], size); + result.SetOffset(result.GetOffset() + size); + + Util::Copy(&result[result.GetOffset()], in->GetPeak(), byteDepth); + + return true; + } + + bool DecodeEHA(const ehs::AudioCodec* const codec, ehs::Serializer& in, ehs::Audio* const out) + { + Version version = in.ReadVersion(); + if (version != Version(1, 0, 0)) + { + EHS_LOG_INT("Error", 0, "Incompatible audio file version."); + return false; + } + + UInt_64 sampleRate = in.Read(); + DataType dataType = in.Read(); + UInt_8 byteDepth = in.Read(); + UInt_8 channels = in.Read(); + UInt_64 frames = in.Read(); + + *out = Audio(out->GetId(), sampleRate, dataType, channels, frames); + + UInt_64 size = out->GetSize(); + Util::Copy(&(*out)[0], &in[in.GetOffset()], size); + in.SetOffset(in.GetOffset() + size); + + out->SetPeak(byteDepth, &in[in.GetOffset()]); + + return true; + } + + bool DecodeWAV(const ehs::AudioCodec* const codec, ehs::Serializer& in, ehs::Audio* out) + { + RIFF riff(in); + + if (riff.GetType() != "WAVE") + { + EHS_LOG_INT("Error", 0, "Data is not in WAVE format."); + return false; + } + + RIFF_Chunk fmt = riff.GetChunk("fmt "); + if (!fmt.IsValid()) + { + EHS_LOG_INT("Error", 1, "Wave does not have a format chunk."); + return false; + } + + Serializer<> fmtSer = fmt.GetData(); + + RIFF_Chunk dChunk = riff.GetChunk("data"); + if (!dChunk.IsValid()) + { + EHS_LOG_INT("Error", 2, "Wave does not have a data chunk."); + return false; + } + + UInt_16 compression = fmtSer.Read(); + if (compression == 0x2) + { + EHS_LOG_INT("Error", 3, "Microsoft ADPCM compression unsupported."); + return false; + } + else if (compression == 0x6) + { + EHS_LOG_INT("Error", 4, "ITU G.711 a-law compression unsupported."); + return false; + } + else if (compression == 0x7) + { + EHS_LOG_INT("Error", 5, "ITU G.711 µ-law compression unsupported."); + return false; + } + else if (compression == 0x11) + { + EHS_LOG_INT("Error", 6, "IMA ADPCM compression unsupported."); + return false; + } + else if (compression == 0x16) + { + EHS_LOG_INT("Error", 7, "TU G.723 ADPCM (Yamaha) compression unsupported."); + return false; + } + else if (compression == 0x31) + { + EHS_LOG_INT("Error", 8, "GSM 6.10 compression unsupported."); + return false; + } + else if (compression == 0x40) + { + EHS_LOG_INT("Error", 9, "ITU G.721 ADPCM compression unsupported."); + return false; + } + else if (compression == 0x50) + { + EHS_LOG_INT("Error", 10, "MPEG compression unsupported."); + return false; + } + else if (compression == 0xFFFF) + { + EHS_LOG_INT("Error", 11, "Experimental compression unsupported."); + return false; + } + else if (compression != 0x1 && compression != 0x3) + { + EHS_LOG_INT("Error", 12, "Wave has unknown compression of " + Str_8::FromNum(compression) + "."); + return false; + } + + UInt_16 channels = fmtSer.Read(); + UInt_32 sampleRate = fmtSer.Read(); + fmtSer.SetOffset(fmtSer.GetOffset() + 6); + UInt_8 byteDepth = (UInt_8)(fmtSer.Read() / 8); + + DataType dataType; + if (byteDepth == 1) + dataType = DataType::SINT_8; + else if (byteDepth == 2) + dataType = DataType::SINT_16; + else if (byteDepth == 3) + dataType = DataType::SINT_24; + else if (byteDepth == 4 && compression == 0x3) + dataType = DataType::FLOAT; + else if (byteDepth == 4) + dataType = DataType::SINT_32; + else if (byteDepth == 8) + dataType = DataType::SINT_64; + else + return false; + + UInt_64 size = dChunk.GetData().Size(); + UInt_64 frames = size / byteDepth / channels; + + *out = std::move(Audio(out->GetId(), sampleRate, dataType, channels, frames)); + + Serializer<> dataSer = dChunk.GetData(); + + for (UInt_32 i = 0; i < dataSer.Size(); i += byteDepth) + { + if (byteDepth == 1) + { + *(SInt_8*)&(*out)[i] = dataSer.Read(); + if ((*out)[i] > *(SInt_8*)out->GetPeak()) + out->SetPeak(sizeof(SInt_8), &(*out)[i]); + } + else if (byteDepth == 2) + { + *(SInt_16*)&(*out)[i] = dataSer.Read(); + if (*(SInt_16*)&(*out)[i] > *(SInt_16*)out->GetPeak()) + out->SetPeak(sizeof(SInt_16), &(*out)[i]); + } + else if (byteDepth == 3) + { + *(SInt_16*)&(*out)[i + 1] = dataSer.Read(); + (*out)[i] = dataSer.Read(); + + SInt_32 signal = 0; + signal |= (*out)[i]; + signal |= (*out)[i + 1] << 8; + signal |= (*out)[i + 2] << 16; + + SInt_32 peak = 0; + peak |= out->GetPeak()[0]; + peak |= out->GetPeak()[1] << 8; + peak |= out->GetPeak()[2] << 16; + + if (signal > peak) + out->SetPeak(3, &(*out)[i]); + } + else if (byteDepth == 4 && compression == 0x3) + { + *(float*)&(*out)[i] = dataSer.Read(); + if (*(float*)&(*out)[i] > *(float*)out->GetPeak()) + out->SetPeak(sizeof(float), &(*out)[i]); + } + else if (byteDepth == 4) + { + *(SInt_32*)&(*out)[i] = dataSer.Read(); + if (*(SInt_32*)&(*out)[i] > *(SInt_32*)out->GetPeak()) + out->SetPeak(sizeof(SInt_32), &(*out)[i]); + } + else if (byteDepth == 8) + { + *(SInt_64*)&(*out)[i] = dataSer.Read(); + if (*(SInt_64*)&(*out)[i] > *(SInt_64*)out->GetPeak()) + out->SetPeak(sizeof(SInt_64), &(*out)[i]); + } + } + + return true; + } } \ No newline at end of file diff --git a/src/io/img/Img.cpp b/src/io/img/Img.cpp index df30c9d..5558fb5 100644 --- a/src/io/img/Img.cpp +++ b/src/io/img/Img.cpp @@ -1,4 +1,7 @@ #include "ehs/io/img/Img.h" +#include "ehs/io/img/PNG.h" + +#include namespace ehs { @@ -53,6 +56,30 @@ namespace ehs AddType("Img"); } + Img::Img(const Str_8& filePath) + : byteDepth(0), channels(0), size(0), data(nullptr) + { + AddType("Img"); + + File file(filePath, Mode::READ, Disposition::OPEN); + Str_8 ext = file.GetExtension(); + hashId = file.GetName().Hash_64(); + id = file.GetName(); + + const ImgCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return; + } + + Serializer inData = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); + + file.Release(); + + codec->Decode(inData, this); + } + Img::Img(Str_8 id, const UInt_8 byteDepth, const UInt_8 channels, const Vec2_u64& resolution, const Byte* const data) : hashId(id.Hash_64()), id((Str_8&&)id), byteDepth(byteDepth), channels(channels), resolution(resolution), size(resolution.x * byteDepth * channels * resolution.y), data(new Byte[size]) @@ -69,12 +96,6 @@ namespace ehs AddType("Img"); } - Img::Img(Str_8 id) - : hashId(id.Hash_64()), id((Str_8&&)id), byteDepth(0), channels(0), size(0), data(nullptr) - { - AddType("Img"); - } - Img::Img(Img&& img) noexcept : BaseObj((BaseObj&&)img), hashId(img.hashId), id((Str_8&&)img.id), byteDepth(img.byteDepth), channels(img.channels), resolution(img.resolution), size(img.size), data(img.data) @@ -849,7 +870,7 @@ namespace ehs return size; } - bool Img::ToFile(const Str_8& filePath) const + bool Img::Export(const Str_8& filePath) const { Str_8 ext = File::ParseExt_8(filePath); @@ -870,76 +891,6 @@ namespace ehs return true; } - Img Img::FromFile(const Str_8& filePath) - { - File file(filePath, Mode::READ, Disposition::OPEN); - Str_8 ext = file.GetExtension(); - - Img result(file.GetName()); - - const ImgCodec* codec = GetCodec(ext); - if (!codec) - { - EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); - return result; - } - - Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); - - file.Release(); - - if (!codec->Decode(data, &result)) - return {}; - - return result; - } - - Img* Img::FromFile_Heap(const Str_8& filePath) - { - File file(filePath, Mode::READ, Disposition::OPEN); - Str_8 ext = file.GetExtension(); - - Img* result = nullptr; - - const ImgCodec* codec = GetCodec(ext); - if (!codec) - { - EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); - return result; - } - - result = new Img(file.GetName()); - - Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); - - file.Release(); - - if (!codec->Decode(data, result)) - { - delete result; - return nullptr; - } - - return result; - } - - Img Img::FromData(Str_8 id, const Str_8& ext, Serializer& data) - { - Img result((Str_8&&)id); - - const ImgCodec* codec = GetCodec(ext); - if (!codec) - { - EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); - return result; - } - - if (!codec->Decode(data, &result)) - return {}; - - return result; - } - Img Img::GetNearestNeighbor(const Vec2_u64& newResolution) const { Img result(id, byteDepth, channels, newResolution); @@ -1485,4 +1436,320 @@ namespace ehs for (UInt_64 i = 0, n = 0; i < newSize; ++i, n += 2) buffer[i] = (Byte)((float)*(UInt_16*)&data[n] / (float)EHS_UINT_16_MAX * (float)EHS_UINT_8_MAX); } + + bool EncodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer& out, const ehs::Img* in) + { + UInt_8 channels = in->GetChannels(); + Vec2_u64 resolution = in->GetResolution(); + + UInt_32 px_len = resolution.x * resolution.y * channels; + UInt_32 px_end = px_len - channels; + + Byte index[256]; + for (UInt_64 i = 0; i < 64; ++i) + *(UInt_32*)&index[i * 4] = 0; + + Byte prevPixel[4] = {0, 0, 0, 255}; + Byte pixel[4] = {0, 0, 0, 255}; + + Serializer result(Endianness::BE, resolution.x * resolution.y * (channels + 1) + 22); + + result.Write('q'); + result.Write('o'); + result.Write('i'); + result.Write('f'); + result.Write(resolution.x); + result.Write(resolution.y); + result.Write(in->GetChannels()); + result.Write(1); + + for (UInt_32 px_pos = 0, run = 0; px_pos < px_len; px_pos += channels) + { + if (channels == 4) + { + *(UInt_32*)pixel = *(UInt_32*)&(*in)[px_pos]; + } + else + { + pixel[0] = (*in)[px_pos]; + pixel[1] = (*in)[px_pos + 1]; + pixel[2] = (*in)[px_pos + 2]; + } + + if (*(UInt_32*)pixel == *(UInt_32*)prevPixel) + { + run++; + if (run == 62 || px_pos == px_end) + { + result.Write(0xc0 | (run - 1)); + run = 0; + } + } + else + { + if (run > 0) + { + result.Write(0xc0 | (run - 1)); + run = 0; + } + + UInt_32 index_pos = (prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels; + + if (*(UInt_32*)&index[index_pos] == *(UInt_32*)pixel) + { + result.Write(0x00 | (index_pos / channels)); + } + else + { + *(UInt_32*)&index[index_pos] = *(UInt_32*)pixel; + + if (pixel[3] == prevPixel[3]) + { + SInt_8 vr = pixel[0] - prevPixel[0]; + SInt_8 vg = pixel[1] - prevPixel[1]; + SInt_8 vb = pixel[2] - prevPixel[2]; + + SInt_8 vg_r = vr - vg; + SInt_8 vg_b = vb - vg; + + if ( + vr > -3 && vr < 2 && + vg > -3 && vg < 2 && + vb > -3 && vb < 2 + ) + { + result.Write(0x40 | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2)); + } + else if ( + vg_r > -9 && vg_r < 8 && + vg > -33 && vg < 32 && + vg_b > -9 && vg_b < 8 + ) + { + result.Write(0x80 | (vg + 32)); + result.Write((vg_r + 8) << 4 | (vg_b + 8)); + } + else + { + result.Write(0xfe); + result.Write(pixel[0]); + result.Write(pixel[1]); + result.Write(pixel[2]); + } + } + else + { + result.Write(0xff); + result.SetEndianness(CPU::GetEndianness()); + result.Write(*(UInt_32*)pixel); + result.SetEndianness(Endianness::BE); + } + } + } + + *(UInt_32*)prevPixel = *(UInt_32*)pixel; + } + + result.Write(0x100000000000000); + + return true; + } + + bool DecodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer& in, ehs::Img* out) + { + Str_8 imgType = in.ReadStr(4); + if (imgType != "qoif") + { + EHS_LOG_INT("Error", 0, "Given data is not in the qoif format."); + return false; + } + + UInt_64 width = in.Read(); + UInt_64 height = in.Read(); + + UInt_8 channels = in.Read(); + channels = 4; + UInt_8 space = in.Read(); + UInt_8 bitDepth = 8; + + UInt_64 size = width * channels * height; + + *out = Img(out->GetId(), bitDepth / 8, channels, {width, height}); + + Byte prevPixel[4] = {0, 0, 0, 255}; + + Byte index[256]; + for (UInt_64 i = 0; i < 64; ++i) + *(UInt_32*)&index[i * 4] = 0; + + UInt_32 chunksLen = in.Size() - 8; + for (UInt_32 pos = 0, run = 0; pos < size; pos += channels) + { + if (run > 0) + --run; + else if (in.GetOffset() < chunksLen) + { + UInt_32 chunkType = (UInt_32)in.Read(); + if (chunkType == 0xfe) // RGB + { + prevPixel[0] = in.Read(); // R-value + prevPixel[1] = in.Read(); // G-value + prevPixel[2] = in.Read(); // B-value + } + else if (chunkType == 0xff) // RGBA + { + *(UInt_32*)prevPixel = in.Read(); + } + else if ((chunkType & 0xc0) == 0x00) // Index + { + *(UInt_32*)prevPixel = *(UInt_32*)&index[chunkType * channels]; + } + else if ((chunkType & 0xc0) == 0x40) // Diff + { + prevPixel[0] += ((chunkType >> 4) & 0x03) - 2; // R-value + prevPixel[1] += ((chunkType >> 2) & 0x03) - 2; // G-value + prevPixel[2] += (chunkType & 0x03) - 2; // B-value + } + else if ((chunkType & 0xc0) == 0x80) // Luma + { + UInt_32 mod = (UInt_32)in.Read(); + UInt_32 vg = (chunkType & 0x3f) - 32; + prevPixel[0] += vg - 8 + ((mod >> 4) & 0x0f); // R-value + prevPixel[1] += vg; // G-value + prevPixel[2] += vg - 8 + (mod & 0x0f); // B-value + } + else if ((chunkType & 0xc0) == 0xc0) // Run + run = (chunkType & 0x3f); + + *(UInt_32*)&index[(prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels] = *(UInt_32*)prevPixel; + } + + if (channels == 4) + { + *((UInt_32*)&(*out)[pos]) = *(UInt_32*)prevPixel; + } + else + { + (*out)[pos] = prevPixel[0]; + (*out)[pos + 1] = prevPixel[1]; + (*out)[pos + 2] = prevPixel[2]; + } + } + + return true; + } + + bool DecodePNG(const ehs::ImgCodec* const codec, ehs::Serializer& in, ehs::Img* out) + { + PNG png(out->GetId(), in); + + PNG_Chunk* ihdr = png.GetChunk("IHDR"); + Serializer* ihdrData = ihdr->GetData(); + + UInt_32 width = ihdrData->Read(); + UInt_32 height = ihdrData->Read(); + UInt_8 bitDepth = ihdrData->Read(); + + UInt_8 colorType = ihdrData->Read(); + if (colorType == 3) + { + EHS_LOG_INT("Error", 1, "Color type of " + Str_8::FromNum(colorType) + " is unsupported."); + return false; + } + + UInt_8 channels = 1; + if (colorType == 2) + channels = 3; + else if (colorType == 4) + channels = 2; + else if (colorType == 6) + channels = 4; + + *out = Img(out->GetId(), bitDepth / 8, channels, {width, height}); + + UInt_8 compression = ihdrData->Read(); + if (compression) + { + EHS_LOG_INT("Error", 2, "Compression method of " + Str_8::FromNum(compression) + " is unsupported."); + return false; + } + + UInt_8 filter = ihdrData->Read(); + if (filter) + { + EHS_LOG_INT("Error", 3, "Filter method of " + Str_8::FromNum(filter) + " is unsupported."); + return false; + } + + UInt_8 interlaced = ihdrData->Read(); + if (interlaced) + { + EHS_LOG_INT("Error", 4, "Interlacing method of " + Str_8::FromNum(interlaced) + " is unsupported."); + return false; + } + + UInt_32 scanline = width * (bitDepth / 8) * channels; + UInt_32 scanLineF = scanline + 1; + UInt_32 bufferSize = scanline * height + height; + + Byte* buffer = new Byte[bufferSize]; + + PNG_Chunk* idat = png.GetChunk("IDAT"); + Serializer* idatData = idat->GetData(); + + z_stream strm = {}; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = idatData->Size(); + strm.next_in = *idatData; + strm.avail_out = bufferSize; + strm.next_out = buffer; + + int code = inflateInit(&strm); + if (code != Z_OK) + { + EHS_LOG_INT("Error", 5, "Failed to initialize zlib inflate with error #" + Str_8::FromNum(code) + "."); + delete[] buffer; + return false; + } + + do + { + code = inflate(&strm, Z_NO_FLUSH); + if (code != Z_STREAM_END && code != Z_OK) + { + EHS_LOG_INT("Error", 6, "Failed to zlib inflate with error #" + Str_8::FromNum(code) + "."); + delete[] buffer; + return false; + } + } while (strm.avail_out); + + code = inflateEnd(&strm); + if (code != Z_OK) + { + EHS_LOG_INT("Error", 7, "Failed to uninitialize zlib inflate with error #" + Str_8::FromNum(code) + "."); + delete[] buffer; + return false; + } + + for (UInt_32 i = 0, o = 0; i < bufferSize; i += scanLineF, o += scanline) + { + UInt_8 fCode = buffer[i]; + if (fCode == 0) + PNG::FilterNone(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline); + else if (fCode == 1) + PNG::FilterSub(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline); + else if (fCode == 2) + PNG::FilterUp(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); + else if (fCode == 3) + PNG::FilterAverage(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); + else if (fCode == 4) + PNG::FilterPaeth(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); + } + + delete[] buffer; + + return true; + } } \ No newline at end of file diff --git a/src/io/img/ImgCodec.cpp b/src/io/img/ImgCodec.cpp index 0281a26..8a86285 100644 --- a/src/io/img/ImgCodec.cpp +++ b/src/io/img/ImgCodec.cpp @@ -4,30 +4,29 @@ namespace ehs { ImgCodec::ImgCodec() - : hashExt(0), endianness(Endianness::LE), encodeCb(nullptr), decodeCb(nullptr) + : hashExt(0), endianness(Endianness::LE), encoder(nullptr), decoder(nullptr) { } - ImgCodec::ImgCodec(Str_8 id, Str_8 ext, const Endianness end, - bool (* encodeCb)(const ImgCodec* const, Serializer&, const Img*), - bool (* decodeCb)(const ImgCodec* const, Serializer&, Img*)) - : id(std::move(id)), hashExt(ext.Hash_64()), ext(std::move(ext)), endianness(end), encodeCb(encodeCb), decodeCb(decodeCb) + ImgCodec::ImgCodec(Str_8 id, Str_8 ext, const Endianness end, EncodeImgCb encoder, DecodeImgCb decoder) + : id(std::move(id)), hashExt(ext.Hash_64()), ext(std::move(ext)), endianness(end), encoder(encoder), + decoder(decoder) { } ImgCodec::ImgCodec(ImgCodec&& codec) noexcept : id(std::move(codec.id)), hashExt(codec.hashExt), ext(std::move(codec.ext)), endianness(codec.endianness), - encodeCb(codec.encodeCb), decodeCb(codec.decodeCb) + encoder(codec.encoder), decoder(codec.decoder) { codec.hashExt = 0; codec.endianness = Endianness::LE; - codec.encodeCb = nullptr; - codec.decodeCb = nullptr; + codec.encoder = nullptr; + codec.decoder = nullptr; } ImgCodec::ImgCodec(const ImgCodec& codec) - : id(codec.id), hashExt(codec.hashExt), ext(codec.ext), endianness(codec.endianness), encodeCb(codec.encodeCb), - decodeCb(codec.decodeCb) + : id(codec.id), hashExt(codec.hashExt), ext(codec.ext), endianness(codec.endianness), encoder(codec.encoder), + decoder(codec.decoder) { } @@ -40,13 +39,13 @@ namespace ehs hashExt = codec.hashExt; ext = std::move(codec.ext); endianness = codec.endianness; - encodeCb = codec.encodeCb; - decodeCb = codec.decodeCb; + encoder = codec.encoder; + decoder = codec.decoder; codec.hashExt = 0; codec.endianness = Endianness::LE; - codec.encodeCb = nullptr; - codec.decodeCb = nullptr; + codec.encoder = nullptr; + codec.decoder = nullptr; return *this; } @@ -60,8 +59,8 @@ namespace ehs hashExt = codec.hashExt; ext = codec.ext; endianness = codec.endianness; - encodeCb = codec.encodeCb; - decodeCb = codec.decodeCb; + encoder = codec.encoder; + decoder = codec.decoder; return *this; } @@ -88,23 +87,23 @@ namespace ehs bool ImgCodec::Encode(Serializer& out, const Img* in) const { - if (!encodeCb) + if (!encoder) { EHS_LOG_INT("Error", 0, "Encoding is not supported for the " + id + " format."); return false; } - return encodeCb(this, out, in); + return encoder(this, out, in); } bool ImgCodec::Decode(Serializer& in, Img* out) const { - if (!decodeCb) + if (!decoder) { EHS_LOG_INT("Error", 0, "Decoding is not supported for the " + id + " format."); return false; } - return decodeCb(this, in, out); + return decoder(this, in, out); } } \ No newline at end of file diff --git a/src/io/model/Model.cpp b/src/io/model/Mdl.cpp similarity index 61% rename from src/io/model/Model.cpp rename to src/io/model/Mdl.cpp index e90f621..3bc7007 100644 --- a/src/io/model/Model.cpp +++ b/src/io/model/Mdl.cpp @@ -1,54 +1,102 @@ -#include "ehs/io/model/Model.h" +#include "ehs/io/mdl/Mdl.h" namespace ehs { - Model::Model() - : hashId(0) + Array Mdl::codecs; + + bool Mdl::HasCodec(const UInt_64 hashExt) { - AddType("Model"); + for (UInt_64 i = 0; i < codecs.Size(); ++i) + if (codecs[i].GetHashExt() == hashExt) + return true; + + return false; } - Model::Model(const Str_8& filePath) + bool Mdl::HasCodec(const Str_8& ext) { - AddType("Model"); + return HasCodec(ext.Hash_64()); + } + + bool Mdl::AddCodec(MdlCodec codec) + { + if (HasCodec(codec.GetHashExt())) + return false; + + codecs.Push(std::move(codec)); + + return true; + } + + const MdlCodec* Mdl::GetCodec(const UInt_64 hashExt) + { + for (UInt_64 i = 0; i < codecs.Size(); ++i) + if (codecs[i].GetHashExt() == hashExt) + return &codecs[i]; + + return nullptr; + } + + const MdlCodec* Mdl::GetCodec(const Str_8& ext) + { + return GetCodec(ext.Hash_64()); + } + + Mdl::Mdl() + : hashId(0) + { + AddType("Mdl"); + } + + Mdl::Mdl(const Str_8& filePath) + { + AddType("Mdl"); File file(filePath, Mode::READ, Disposition::OPEN); - + Str_8 ext = file.GetExtension(); hashId = file.GetName().Hash_64(); id = file.GetName(); - if (file.GetExtension() == "ehm") + const MdlCodec* codec = GetCodec(ext); + if (!codec) { - FromEHM(file); + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return; } + + Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); + + file.Release(); + + codec->Decode(data, this); } - Model::Model(Str_8 id, Array meshes, Bone skeleton, Array animations) + Mdl::Mdl(Str_8 id, Array meshes, Bone skeleton, Array animations) : hashId(id.Hash_64()), id((Str_8&&)id), meshes((Array&&)meshes), skeleton((Bone&&)skeleton), animations((Array&&)animations) { - AddType("Model"); + AddType("Mdl"); } - Model::Model(Str_8 id, Array meshes, Bone skeleton) + Mdl::Mdl(Str_8 id, Array meshes, Bone skeleton) : hashId(id.Hash_64()), id((Str_8&&)id), meshes((Array&&)meshes), skeleton((Bone&&)skeleton) { - AddType("Model"); + AddType("Mdl"); } - Model::Model(Str_8 id, Array meshes) + Mdl::Mdl(Str_8 id, Array meshes) : hashId(id.Hash_64()), id((Str_8&&)id), meshes((Array&&)meshes) { - AddType("Model"); + AddType("Mdl"); } - Model::Model(Model&& model) noexcept + Mdl::Mdl(Mdl&& model) noexcept : BaseObj((BaseObj&&)model), hashId(model.hashId), id((Str_8&&)model.id), meshes((Array&&)model.meshes), skeleton((Bone&&)model.skeleton), animations((Array&&)model.animations) { } - Model& Model::operator=(Model&& model) noexcept + Mdl& Mdl::operator=(Mdl&& model) noexcept { if (this == &model) return *this; @@ -66,40 +114,40 @@ namespace ehs return *this; } - void Model::Release() + void Mdl::Release() { meshes.Clear(); skeleton = {}; animations.Clear(); } - UInt_64 Model::GetHashId() const + UInt_64 Mdl::GetHashId() const { return hashId; } - void Model::SetId(Str_8 newId) + void Mdl::SetId(Str_8 newId) { hashId = newId.Hash_64(); id = (Str_8&&)newId; } - Str_8 Model::GetId() const + Str_8 Mdl::GetId() const { return id; } - const Array& Model::GetMeshes() const + const Array& Mdl::GetMeshes() const { return meshes; } - Array& Model::GetMeshes() + Array& Mdl::GetMeshes() { return meshes; } - Mesh* Model::GetMesh(const UInt_64 inHashId) + Mesh* Mdl::GetMesh(const UInt_64 inHashId) { for (UInt_64 i = 0; i < meshes.Size(); ++i) if (meshes[i].GetHashId() == inHashId) @@ -108,22 +156,22 @@ namespace ehs return nullptr; } - Mesh* Model::GetMesh(const Str_8& inId) + Mesh* Mdl::GetMesh(const Str_8& inId) { return GetMesh(inId.Hash_64()); } - const Bone& Model::GetSkeleton() const + const Bone& Mdl::GetSkeleton() const { return skeleton; } - Bone& Model::GetSkeleton() + Bone& Mdl::GetSkeleton() { return skeleton; } - Animation* Model::GetAnimation(const UInt_64 inHashId) + Animation* Mdl::GetAnimation(const UInt_64 inHashId) { for (UInt_64 i = 0; i < animations.Size(); ++i) if (animations[i].GetHashId() == inHashId) @@ -132,39 +180,47 @@ namespace ehs return nullptr; } - const Array& Model::GetAnimations() const + const Array& Mdl::GetAnimations() const { return animations; } - Array& Model::GetAnimations() + Array& Mdl::GetAnimations() { return animations; } - void Model::Calculate() + void Mdl::Calculate() { for (UInt_64 i = 0; i < meshes.Size(); ++i) meshes[i].Calculate(); } - void Model::Export(const Str_8& filePath, const ModelEncoding encoding) + bool Mdl::Export(const Str_8& filePath) const { - File file(filePath, Mode::WRITE, Disposition::OPEN_PERSISTENT); + Str_8 ext = File::ParseExt_8(filePath); - switch (encoding) + const MdlCodec* codec = GetCodec(ext); + if (!codec) { - case ModelEncoding::EHM: - { - ToEHM(file); - break; - } + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return false; } + + Serializer result; + if (!codec->Encode(result, this)) + return false; + + File file(filePath, Mode::WRITE, Disposition::CREATE_PERSISTENT); + file.WriteSerializer_64(result); + + return true; } - void Model::ToEHM(File& file) + bool EncodeEHM(const MdlCodec* const codec, Serializer& data, const Mdl* const mdl) { - Serializer data(Endianness::LE); + const Array& meshes = mdl->GetMeshes(); + data.WriteVersion({1, 0, 0}); data.Write(meshes.Size()); @@ -184,24 +240,23 @@ namespace ehs } } - file.WriteSerializer_64(data); - file.Truncate(data.Size()); + return true; } - void Model::FromEHM(File& file) + bool DecodeEHM(const MdlCodec* const codec, Serializer& data, Mdl* const mdl) { - Serializer data = file.ReadSerializer_64(Endianness::LE, file.Size()); - Version ver = data.ReadVersion(); if (ver != Version(1, 0, 0)) { EHS_LOG_INT("Error", 0, "Cannot decode EHM file version " + Str_8::FromNum(ver.major) + "." + Str_8::FromNum(ver.minor) + "." + Str_8::FromNum(ver.patch) + ", must be version 0.0.0."); - return; + return false; } + Array& meshes = mdl->GetMeshes(); meshes.Resize(data.Read()); + for (UInt_64 i = 0; i < meshes.Size(); ++i) { meshes[i].SetId(data.ReadStr()); @@ -220,6 +275,7 @@ namespace ehs meshes[i].SetIndices(data.ReadArray()); + Bone& skeleton = mdl->GetSkeleton(); UInt_8 boneCount = data.Read(); for (UInt_8 b = 0; b < boneCount; ++b) { @@ -237,14 +293,16 @@ namespace ehs if (!parent) { EHS_LOG_INT("Error", 0, "Invalid bone order."); - return; + return false; } parent->AddBone({name, b, localBindTrans, invBindTrans}); } } - animations = Array(data.Read()); + Array& animations = mdl->GetAnimations(); + animations.Resize(data.Read()); + for (UInt_64 a = 0; a < animations.Size(); ++a) { Str_8 animId = data.ReadStr(); @@ -271,5 +329,7 @@ namespace ehs } } } + + return true; } } \ No newline at end of file diff --git a/src/io/model/MdlCodec.cpp b/src/io/model/MdlCodec.cpp new file mode 100644 index 0000000..52aec6e --- /dev/null +++ b/src/io/model/MdlCodec.cpp @@ -0,0 +1,108 @@ +#include "ehs/io/mdl/MdlCodec.h" + +namespace ehs +{ + MdlCodec::MdlCodec() + : hashExt(0), endianness(Endianness::LE), encoder(nullptr), decoder(nullptr) + { + } + + MdlCodec::MdlCodec(Str_8 id, Str_8 ext, const Endianness end, EnocdeMdlCb encoder, DecodeMdlCb decoder) + : id(std::move(id)), hashExt(ext.Hash_64()), ext(std::move(ext)), endianness(end), encoder(encoder), + decoder(decoder) + { + } + + MdlCodec::MdlCodec(MdlCodec&& codec) noexcept + : id(std::move(codec.id)), hashExt(codec.hashExt), ext(std::move(codec.ext)), endianness(codec.endianness), + encoder(codec.encoder), decoder(codec.decoder) + { + codec.hashExt = 0; + codec.endianness = Endianness::LE; + codec.encoder = nullptr; + codec.decoder = nullptr; + } + + MdlCodec::MdlCodec(const MdlCodec& codec) + : id(codec.id), hashExt(codec.hashExt), ext(codec.ext), endianness(codec.endianness), encoder(codec.encoder), + decoder(codec.decoder) + { + } + + MdlCodec& MdlCodec::operator=(MdlCodec&& codec) noexcept + { + if (this == &codec) + return *this; + + id = std::move(codec.id); + hashExt = codec.hashExt; + ext = std::move(codec.ext); + endianness = codec.endianness; + encoder = codec.encoder; + decoder = codec.decoder; + + codec.hashExt = 0; + codec.endianness = Endianness::LE; + codec.encoder = nullptr; + codec.decoder = nullptr; + + return *this; + } + + MdlCodec& MdlCodec::operator=(const MdlCodec& codec) + { + if (this == &codec) + return *this; + + id = codec.id; + hashExt = codec.hashExt; + ext = codec.ext; + endianness = codec.endianness; + encoder = codec.encoder; + decoder = codec.decoder; + + return *this; + } + + Str_8 MdlCodec::GetId() const + { + return id; + } + + UInt_64 MdlCodec::GetHashExt() const + { + return hashExt; + } + + Str_8 MdlCodec::GetExt() const + { + return ext; + } + + Endianness MdlCodec::GetEndianness() const + { + return endianness; + } + + bool MdlCodec::Encode(Serializer& out, const Mdl* in) const + { + if (!encoder) + { + EHS_LOG_INT("Error", 0, "Encoding is not supported for the " + id + " format."); + return false; + } + + return encoder(this, out, in); + } + + bool MdlCodec::Decode(Serializer& in, Mdl* out) const + { + if (!decoder) + { + EHS_LOG_INT("Error", 0, "Decoding is not supported for the " + id + " format."); + return false; + } + + return decoder(this, in, out); + } +} \ No newline at end of file