Add bclim support
This commit is contained in:
@@ -22,13 +22,14 @@ if(${CTRFF_3DS})
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(ctrff STATIC
|
add_library(ctrff STATIC
|
||||||
|
source/3dsx.cpp
|
||||||
|
source/bclim.cpp
|
||||||
|
source/bcstm.cpp
|
||||||
|
source/bcwav.cpp
|
||||||
|
source/binutil.cpp
|
||||||
source/helper.cpp
|
source/helper.cpp
|
||||||
source/lz11.cpp
|
source/lz11.cpp
|
||||||
source/smdh.cpp
|
source/smdh.cpp
|
||||||
source/binutil.cpp
|
|
||||||
source/bcstm.cpp
|
|
||||||
source/bcwav.cpp
|
|
||||||
source/3dsx.cpp
|
|
||||||
)
|
)
|
||||||
target_include_directories(ctrff PUBLIC include)
|
target_include_directories(ctrff PUBLIC include)
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Not all Planned formates are listed here yet
|
|||||||
| 3dsx | Basic Loading and Viewing of Meta Data Smdh | |
|
| 3dsx | Basic Loading and Viewing of Meta Data Smdh | |
|
||||||
| bcstm | Loading of almost every Data | Fully done and playable by BCSTM-Player |
|
| bcstm | Loading of almost every Data | Fully done and playable by BCSTM-Player |
|
||||||
| bcwav | Basic Loading (not tested yet) | Not finished yet |
|
| bcwav | Basic Loading (not tested yet) | Not finished yet |
|
||||||
| bclim | Nothing done yet (Started creating header) | |
|
| bclim | Creating A8,RGBA32,RGB565 done | WIP |
|
||||||
| lz11 | Encoder done, Decoder missing | WIP |
|
| lz11 | Encoder done, Decoder missing | WIP |
|
||||||
| romfs | Nothing Done yet (Started creating header) | |
|
| romfs | Nothing Done yet (Started creating header) | |
|
||||||
| smdh | Almost done | missing safetey checks |
|
| smdh | Almost done | missing safetey checks |
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ctrff/3dsx.hpp>
|
#include <ctrff/3dsx.hpp>
|
||||||
|
#include <ctrff/bclim.hpp>
|
||||||
#include <ctrff/bcstm.hpp>
|
#include <ctrff/bcstm.hpp>
|
||||||
#include <ctrff/bcwav.hpp>
|
#include <ctrff/bcwav.hpp>
|
||||||
#include <ctrff/binutil.hpp>
|
#include <ctrff/binutil.hpp>
|
||||||
#include <ctrff/lz11.hpp>
|
#include <ctrff/lz11.hpp>
|
||||||
#include <ctrff/smdh.hpp>
|
#include <ctrff/smdh.hpp>
|
||||||
94
include/ctrff/bclim.hpp
Normal file
94
include/ctrff/bclim.hpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <ctrff/binutil.hpp>
|
||||||
|
#include <ctrff/helper.hpp>
|
||||||
|
#include <ctrff/types.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace ctrff {
|
||||||
|
class CTRFF_API BCLIM : public BinFile {
|
||||||
|
public:
|
||||||
|
BCLIM() {}
|
||||||
|
~BCLIM() {}
|
||||||
|
|
||||||
|
enum Format : u32 {
|
||||||
|
L8,
|
||||||
|
A8, // tested
|
||||||
|
LA4,
|
||||||
|
LA8,
|
||||||
|
HILO8,
|
||||||
|
RGB565, // tested
|
||||||
|
RGB888,
|
||||||
|
RGBA5551,
|
||||||
|
RGBA4444,
|
||||||
|
RGBA8888, // tested
|
||||||
|
ETC1,
|
||||||
|
ETC1A4,
|
||||||
|
L4,
|
||||||
|
A4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Header {
|
||||||
|
static Header Default() {
|
||||||
|
Header h;
|
||||||
|
h.Magic = 0x4d494c43;
|
||||||
|
h.Endianness = 0xfeff;
|
||||||
|
h.HeaderSize = 0x14;
|
||||||
|
h.Version = 0x2020000;
|
||||||
|
h.FileSize = 0;
|
||||||
|
h.NumSections = 0;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
u32 Magic; // 0x4d494c43 "CLIM"
|
||||||
|
u16 Endianness;
|
||||||
|
u16 HeaderSize;
|
||||||
|
u32 Version;
|
||||||
|
u32 FileSize;
|
||||||
|
u32 NumSections;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImagHeader {
|
||||||
|
static ImagHeader Default() {
|
||||||
|
ImagHeader h;
|
||||||
|
h.Magic = 0x67616d69;
|
||||||
|
h.HeaderSize = 0x14;
|
||||||
|
h.Width = 0;
|
||||||
|
h.Height = 0;
|
||||||
|
h.Format = A8;
|
||||||
|
h.ImageSize = 0;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
u32 Magic; // 0x67616d69 "imag"
|
||||||
|
u32 HeaderSize;
|
||||||
|
u16 Width;
|
||||||
|
u16 Height;
|
||||||
|
u32 Format; // A bit waste of data lol
|
||||||
|
u32 ImageSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Load(const std::string& path) {
|
||||||
|
std::fstream f(path, std::ios::in | std::ios::binary);
|
||||||
|
Read(f);
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Save(const std::string& path) {
|
||||||
|
std::fstream f(path, std::ios::out | std::ios::binary);
|
||||||
|
Write(f);
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateByImage(const std::vector<u8>& data, int w, int h, Format fmt);
|
||||||
|
|
||||||
|
/** Write not supported btw */
|
||||||
|
void Write(std::fstream& f) const override;
|
||||||
|
void Read(std::fstream& f) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<u8> pBuffer;
|
||||||
|
Header pCurrent;
|
||||||
|
ImagHeader pImag;
|
||||||
|
bool CreateMode = false;
|
||||||
|
};
|
||||||
|
} // namespace ctrff
|
||||||
@@ -12,4 +12,5 @@ CTRFF_API void RGBA2RGB565(ctrff::u16 *out, const std::vector<ctrff::u8> &img,
|
|||||||
const int &w, const int &h);
|
const int &w, const int &h);
|
||||||
CTRFF_API std::vector<ctrff::u8> DownscaleImage(
|
CTRFF_API std::vector<ctrff::u8> DownscaleImage(
|
||||||
const std::vector<ctrff::u8> &img, int w, int h, int scale);
|
const std::vector<ctrff::u8> &img, int w, int h, int scale);
|
||||||
|
CTRFF_API ctrff::u32 TileIndex(const int &x, const int &y, const int &w);
|
||||||
} // namespace ctrff
|
} // namespace ctrff
|
||||||
55
source/bclim.cpp
Normal file
55
source/bclim.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include <ctrff/bclim.hpp>
|
||||||
|
|
||||||
|
namespace ctrff {
|
||||||
|
CTRFF_API void BCLIM::Write(std::fstream& f) const {
|
||||||
|
f.write(reinterpret_cast<const char*>(pBuffer.data()), pBuffer.size());
|
||||||
|
f.write((const char*)&pCurrent.Magic, sizeof(pCurrent.Magic));
|
||||||
|
f.write((const char*)&pCurrent.Endianness, sizeof(pCurrent.Endianness));
|
||||||
|
f.write((const char*)&pCurrent.HeaderSize, sizeof(pCurrent.HeaderSize));
|
||||||
|
f.write((const char*)&pCurrent.Version, sizeof(pCurrent.Version));
|
||||||
|
f.write((const char*)&pCurrent.FileSize, sizeof(pCurrent.FileSize));
|
||||||
|
f.write((const char*)&pCurrent.NumSections, sizeof(pCurrent.NumSections));
|
||||||
|
f.write((const char*)&pImag.Magic, sizeof(pImag.Magic));
|
||||||
|
f.write((const char*)&pImag.HeaderSize, sizeof(pImag.HeaderSize));
|
||||||
|
f.write((const char*)&pImag.Width, sizeof(pImag.Width));
|
||||||
|
f.write((const char*)&pImag.Height, sizeof(pImag.Height));
|
||||||
|
f.write((const char*)&pImag.Format, sizeof(pImag.Format));
|
||||||
|
f.write((const char*)&pImag.ImageSize, sizeof(pImag.ImageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
CTRFF_API void BCLIM::Read(std::fstream& f) {
|
||||||
|
f.seekg(std::ios::end);
|
||||||
|
size_t size = f.tellg();
|
||||||
|
if (size < (sizeof(Header) + sizeof(ImagHeader))) {
|
||||||
|
throw std::runtime_error("Invalid File!");
|
||||||
|
}
|
||||||
|
f.seekg(size - sizeof(Header) - sizeof(ImagHeader));
|
||||||
|
Header h;
|
||||||
|
ImagHeader imag;
|
||||||
|
f.read(reinterpret_cast<char*>(&h), sizeof(h));
|
||||||
|
f.read(reinterpret_cast<char*>(&imag), sizeof(imag));
|
||||||
|
if (h.Magic != 0x4d494c43) {
|
||||||
|
throw std::runtime_error("[ctrff] BCLIM: Not a bclim file!");
|
||||||
|
}
|
||||||
|
if (imag.Magic != 0x67616d69) {
|
||||||
|
throw std::runtime_error("[ctrff] BCLIM: Invalid Data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CTRFF_API void BCLIM::CreateByImage(const std::vector<u8>& data, int w, int h,
|
||||||
|
Format fmt) {
|
||||||
|
CreateMode = true;
|
||||||
|
pImag = ImagHeader::Default();
|
||||||
|
pCurrent = Header::Default();
|
||||||
|
pImag.Format = fmt;
|
||||||
|
pImag.Width = w;
|
||||||
|
pImag.Height = h;
|
||||||
|
pImag.ImageSize = data.size();
|
||||||
|
pBuffer.resize(data.size());
|
||||||
|
for (int i = 0; i < data.size(); i++) {
|
||||||
|
pBuffer[i] = data[i];
|
||||||
|
}
|
||||||
|
pCurrent.FileSize = pBuffer.size() + pCurrent.HeaderSize + pImag.HeaderSize;
|
||||||
|
pCurrent.NumSections = 1;
|
||||||
|
}
|
||||||
|
} // namespace ctrff
|
||||||
@@ -19,7 +19,8 @@ CTRFF_API ctrff::u16 MakePixel565(const ctrff::u8 &r, const ctrff::u8 &g,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
CTRFF_API ctrff::u32 TileIndex(const int &x, const int &y, const int &w) {
|
CTRFF_API ctrff::u32 ctrff::TileIndex(const int &x, const int &y,
|
||||||
|
const int &w) {
|
||||||
return (((y >> 3) * (w >> 3) + (x >> 3)) << 6) +
|
return (((y >> 3) * (w >> 3) + (x >> 3)) << 6) +
|
||||||
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
|
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
|
||||||
((x & 4) << 2) | ((y & 4) << 3));
|
((x & 4) << 2) | ((y & 4) << 3));
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <palladium>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -442,6 +443,73 @@ void LZ11Compress(const cf7::command::ArgumentList &data) {
|
|||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BCLIMMaker(const cf7::command::ArgumentList &data) {
|
||||||
|
std::string i = cf7::command::GetArg(data, "input");
|
||||||
|
std::string o = cf7::command::GetArg(data, "output");
|
||||||
|
std::string f = cf7::command::GetArg(data, "format");
|
||||||
|
if (i.empty() || o.empty()) {
|
||||||
|
std::cout << "[ctrff] BCLIM: Error, no input or output" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (f.empty() || (f.compare("rgba32") != 0 && f.compare("a8") != 0 &&
|
||||||
|
f.compare("rgb565") != 0)) {
|
||||||
|
f = "a8";
|
||||||
|
}
|
||||||
|
int w = 0, h = 0, c = 0;
|
||||||
|
auto ret = stbi_load(i.c_str(), &w, &h, &c, 4);
|
||||||
|
if (!ret) {
|
||||||
|
std::cout << "[ctrff] BCLIM: Failed to load image " + i << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!PD::BitUtil::IsSingleBit(w) || !PD::BitUtil::IsSingleBit(h)) {
|
||||||
|
std::cout << "[ctrff] BCLIM: Image with and height must be a power of 8!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t size = w * h;
|
||||||
|
if (f == "rgba32") {
|
||||||
|
size *= 4;
|
||||||
|
} else if (f == "rgb565") {
|
||||||
|
size *= 2;
|
||||||
|
}
|
||||||
|
std::vector<ctrff::u8> res(size);
|
||||||
|
if (f == "rgba32" || f == "a8") {
|
||||||
|
for (int x = 0; x < w; x++) {
|
||||||
|
for (int y = 0; y < h; y++) {
|
||||||
|
int src = (y * w + x) * 4;
|
||||||
|
int dst = ctrff::TileIndex(x, y, w);
|
||||||
|
if (f == "rgba32") {
|
||||||
|
dst *= 4;
|
||||||
|
res[dst + 3] = ret[src + 0];
|
||||||
|
res[dst + 2] = ret[src + 1];
|
||||||
|
res[dst + 1] = ret[src + 2];
|
||||||
|
res[dst + 0] = ret[src + 3];
|
||||||
|
} else {
|
||||||
|
res[dst] = ret[src + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (f == "rgb565") {
|
||||||
|
std::vector<ctrff::u16> res16(w * h);
|
||||||
|
ctrff::RGBA2RGB565(res16.data(),
|
||||||
|
std::vector<ctrff::u8>(ret, ret + (w * h * 4)), w, h);
|
||||||
|
for (int i = 0; i < res16.size(); i++) {
|
||||||
|
int pos = i * 2;
|
||||||
|
res[pos] = (ctrff::u8)(res16[i] & 0xff);
|
||||||
|
res[pos + 1] = (ctrff::u8)((res16[i] >> 8) & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctrff::BCLIM file;
|
||||||
|
ctrff::BCLIM::Format fmt = ctrff::BCLIM::A8;
|
||||||
|
if (f == "rgba32") {
|
||||||
|
fmt = ctrff::BCLIM::RGBA8888;
|
||||||
|
} else if (f == "rgb565") {
|
||||||
|
fmt = ctrff::BCLIM::RGB565;
|
||||||
|
}
|
||||||
|
file.CreateByImage(res, w, h, fmt);
|
||||||
|
file.Save(o);
|
||||||
|
std::cout << "File " + o + " created" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
cf7::fancy_print = false;
|
cf7::fancy_print = false;
|
||||||
cf7::colors_supported = false;
|
cf7::colors_supported = false;
|
||||||
@@ -497,6 +565,14 @@ int main(int argc, char *argv[]) {
|
|||||||
cf7::command("hex", "Show Hex view of a File")
|
cf7::command("hex", "Show Hex view of a File")
|
||||||
.AddSubEntry(cf7::command::sub("i", "input", "Input File path", true))
|
.AddSubEntry(cf7::command::sub("i", "input", "Input File path", true))
|
||||||
.SetFunction(Hex));
|
.SetFunction(Hex));
|
||||||
|
mgr.AddCommand(
|
||||||
|
cf7::command("makebclim", "Create CTR Layout Image")
|
||||||
|
.AddSubEntry(cf7::command::sub("i", "input", "Input png|bmp", true))
|
||||||
|
.AddSubEntry(cf7::command::sub("o", "output",
|
||||||
|
"Output path of .bclim file", true))
|
||||||
|
.AddSubEntry(cf7::command::sub("f", "format",
|
||||||
|
"Image format rgba32|rgb565|a8", true))
|
||||||
|
.SetFunction(BCLIMMaker));
|
||||||
mgr.Execute();
|
mgr.Execute();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user