Compare commits

...
3 Commits
Author SHA1 Message Date
tobid7 866acf32e0 Add bclim support 2025-12-29 21:38:07 +01:00
tobid7 6bac17863d Disable coloring and update cli lib 2025-12-29 14:10:03 +01:00
tobid7 ec6968fcb4 FIx issues with lz11 compression (Still WIP)
also fixed compiling on mingw for now
Readded palladium for the desktop tools
2025-12-29 14:02:46 +01:00
13 changed files with 293 additions and 47 deletions
+3
View File
@@ -1,3 +1,6 @@
[submodule "vendor/cli-fancy"]
path = vendor/cli-fancy
url = https://dev.npid7.de/tobid7/cli-fancy
[submodule "vendor/palladium"]
path = vendor/palladium
url = https://github.com/tobid7/palladium
+7 -5
View File
@@ -22,20 +22,22 @@ if(${CTRFF_3DS})
endif()
add_library(ctrff STATIC
source/3dsx.cpp
source/bclim.cpp
source/bcstm.cpp
source/bcwav.cpp
source/binutil.cpp
source/helper.cpp
source/lz11.cpp
source/smdh.cpp
source/binutil.cpp
source/bcstm.cpp
source/bcwav.cpp
source/3dsx.cpp
)
target_include_directories(ctrff PUBLIC include)
if(${CTRFF_DESKTOP})
add_subdirectory(vendor/palladium)
add_executable(ctrff-cli tool/main.cpp)
target_include_directories(ctrff-cli PUBLIC include vendor/stb vendor/cli-fancy/include)
target_link_libraries(ctrff-cli PUBLIC ctrff)
target_link_libraries(ctrff-cli PUBLIC ctrff palladium)
add_executable(test tool/test.cpp)
target_include_directories(test PUBLIC include vendor/stb vendor/cli-fancy/include)
target_link_libraries(test PUBLIC ctrff)
+2 -2
View File
@@ -37,8 +37,8 @@ Not all Planned formates are listed here yet
| 3dsx | Basic Loading and Viewing of Meta Data Smdh | |
| bcstm | Loading of almost every Data | Fully done and playable by BCSTM-Player |
| bcwav | Basic Loading (not tested yet) | Not finished yet |
| bclim | Nothing done yet (Started creating header) | |
| lz11 | Encoder done, Decoder missing | Files are bit diffrent to the ones bannertool generates (don't know why) |
| bclim | Creating A8,RGBA32,RGB565 done | WIP |
| lz11 | Encoder done, Decoder missing | WIP |
| romfs | Nothing Done yet (Started creating header) | |
| smdh | Almost done | missing safetey checks |
| cbmd | Nothing done yet | |
+2 -1
View File
@@ -1,8 +1,9 @@
#pragma once
#include <ctrff/3dsx.hpp>
#include <ctrff/bclim.hpp>
#include <ctrff/bcstm.hpp>
#include <ctrff/bcwav.hpp>
#include <ctrff/binutil.hpp>
#include <ctrff/lz11.hpp>
#include <ctrff/smdh.hpp>
#include <ctrff/smdh.hpp>
+94
View 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
+1
View File
@@ -12,4 +12,5 @@ CTRFF_API void RGBA2RGB565(ctrff::u16 *out, const std::vector<ctrff::u8> &img,
const int &w, const int &h);
CTRFF_API std::vector<ctrff::u8> DownscaleImage(
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
+3 -3
View File
@@ -23,7 +23,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
#ifdef _WIN32 // Windows (MSVC Tested)
#ifdef CTRFF_BUILD_SHARED
#define CTRFF_API __declspec(dllexport)
@@ -45,9 +45,9 @@ SOFTWARE.
#elif defined(__3DS__) // 3ds Specific
// Only Static supported
#define CTRFF_API
#else
#else*/
#define CTRFF_API
#endif
//#endif
#include <cstddef>
#include <format>
+55
View 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
+2 -1
View File
@@ -19,7 +19,8 @@ CTRFF_API ctrff::u16 MakePixel565(const ctrff::u8 &r, const ctrff::u8 &g,
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) +
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
((x & 4) << 2) | ((y & 4) << 3));
+44 -32
View File
@@ -5,8 +5,8 @@
namespace ctrff {
namespace LZ11 {
ctrff::u32 GetOccurenceLength(const ctrff::u8* new_ptr, size_t new_len,
const ctrff::u8* old_ptr, size_t old_len,
ctrff::u32 GetOccurenceLength(const ctrff::u8* new_ptr, u32 new_len,
const ctrff::u8* old_ptr, u32 old_len,
ctrff::u32& disp) {
disp = 0;
if (new_len == 0) {
@@ -14,11 +14,11 @@ ctrff::u32 GetOccurenceLength(const ctrff::u8* new_ptr, size_t new_len,
}
ctrff::u32 res = 0;
if (old_len > 0) {
for (size_t i = 0; i < old_len - 1; i++) {
for (u32 i = 0; i < old_len - 1; i++) {
auto ref = old_ptr + i;
size_t len = 0;
for (size_t j = 0; j < new_len; j++) {
if (*(ref + j) != (*new_ptr + j)) {
u32 len = 0;
for (u32 j = 0; j < new_len; j++) {
if (*(ref + j) != *(new_ptr + j)) {
break;
}
len++;
@@ -41,32 +41,41 @@ CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
return std::vector<ctrff::u8>();
}
std::stringstream s;
struct header {
u8 Magic;
u8 Size1;
u8 Size2;
u8 Size3;
};
header hdr;
hdr.Magic = 0x11;
hdr.Size1 = in.size() & 0xFF;
hdr.Size2 = (in.size() >> 8) & 0xFF;
hdr.Size3 = (in.size() >> 16) & 0xFF;
// SETUP HEADER //
s << static_cast<ctrff::u8>(0x11);
s << static_cast<ctrff::u8>(in.size() & 0xFF);
s << static_cast<ctrff::u8>((in.size() >> 8) & 0xFF);
s << static_cast<ctrff::u8>((in.size() >> 16) & 0xFF);
s.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr));
size_t res_len = 4; // 4-byte header
u32 res_len = 4; // 4-byte header
ctrff::u8 out_buf[0x21]; // 33 bytes
out_buf[0] = 0;
size_t obl = 1; // out_buf_len
size_t buf_blocks = 0;
size_t rb = 0;
memset(out_buf, 0x0, sizeof(out_buf));
u32 obl = 1; // out_buf_len
u32 buf_blocks = 0;
u32 rb = 0;
while (rb < in.size()) {
if (buf_blocks == 8 || obl >= sizeof(out_buf) - 3) {
if (buf_blocks == 8) {
s.write(reinterpret_cast<const char*>(out_buf), obl);
res_len += obl;
out_buf[0] = 0;
memset(out_buf, 0x0, sizeof(out_buf));
obl = 1;
buf_blocks = 0;
}
ctrff::u32 disp = 0;
size_t old_len = std::min(rb, static_cast<size_t>(0x1000));
size_t len = LZ11::GetOccurenceLength(
in.data() + rb, std::min(in.size() - rb, static_cast<size_t>(0x10110)),
u32 old_len = std::min(rb, static_cast<u32>(0x1000));
u32 len = LZ11::GetOccurenceLength(
in.data() + rb,
std::min((u32)in.size() - rb, static_cast<u32>(0x10110)),
in.data() + rb - old_len, old_len, disp);
if (len < 3) {
@@ -75,20 +84,24 @@ CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
rb += len;
out_buf[0] |= static_cast<ctrff::u8>(1 << (7 - buf_blocks));
if (len > 0x110) {
out_buf[obl++] =
0x10 | static_cast<ctrff::u8>(((len - 0x111) >> 12) & 0x0F);
out_buf[obl++] = static_cast<ctrff::u8>(((len - 0x111) >> 4) & 0xFF);
out_buf[obl] = 0x10;
out_buf[obl] |= static_cast<ctrff::u8>(((len - 0x111) >> 12) & 0x0F);
obl++;
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x111) >> 4) & 0xFF);
obl++;
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x111) << 4) & 0xF0);
} else if (len > 0x10) {
out_buf[obl++] =
0x00 | static_cast<ctrff::u8>(((len - 0x11) >> 4) & 0x0F);
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x11) << 4) & 0xF0);
out_buf[obl] = 0x00;
out_buf[obl] |= static_cast<ctrff::u8>(((len - 0x111) >> 4) & 0x0F);
obl++;
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x111) << 4) & 0xF0);
} else {
out_buf[obl] |= static_cast<ctrff::u8>(((len - 1) << 4) & 0xF0);
out_buf[obl] = static_cast<ctrff::u8>(((len - 1) << 4) & 0xF0);
}
out_buf[obl] |= static_cast<ctrff::u8>(((disp - 1) >> 8) & 0x0F);
obl++;
out_buf[obl] = static_cast<ctrff::u8>((disp - 1) & 0xFF);
obl++;
out_buf[obl++] = static_cast<ctrff::u8>(((disp - 1) >> 8) & 0x0F);
out_buf[obl++] = static_cast<ctrff::u8>((disp - 1) & 0xFF);
}
buf_blocks++;
}
@@ -105,9 +118,8 @@ CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
res_len += pad_len;
}
std::vector<ctrff::u8> res(res_len);
s.read(reinterpret_cast<char*>(res.data()), res.size());
return res;
std::string tmp = s.str();
return std::vector<u8>(tmp.begin(), tmp.end());
}
} // namespace LZ11
} // namespace ctrff
+78 -2
View File
@@ -5,6 +5,7 @@
#include <fstream>
#include <iomanip>
#include <map>
#include <palladium>
#include <string>
#include <utility>
@@ -442,9 +443,76 @@ void LZ11Compress(const cf7::command::ArgumentList &data) {
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[]) {
cf7::fancy_print = true;
cf7::colors_supported = true;
cf7::fancy_print = false;
cf7::colors_supported = false;
cf7::arg_mgr mgr(argc, argv);
mgr.SetAppInfo("ctrff", "1.0.0");
auto makesmdh_cmd = cf7::command("makesmdh", "Create a SMDH File");
@@ -497,6 +565,14 @@ int main(int argc, char *argv[]) {
cf7::command("hex", "Show Hex view of a File")
.AddSubEntry(cf7::command::sub("i", "input", "Input File path", true))
.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();
return 0;
}
Vendored Submodule
+1
Submodule vendor/palladium added at 3575a6787d