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"] [submodule "vendor/cli-fancy"]
path = vendor/cli-fancy path = vendor/cli-fancy
url = https://dev.npid7.de/tobid7/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() 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)
if(${CTRFF_DESKTOP}) if(${CTRFF_DESKTOP})
add_subdirectory(vendor/palladium)
add_executable(ctrff-cli tool/main.cpp) add_executable(ctrff-cli tool/main.cpp)
target_include_directories(ctrff-cli PUBLIC include vendor/stb vendor/cli-fancy/include) 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) add_executable(test tool/test.cpp)
target_include_directories(test PUBLIC include vendor/stb vendor/cli-fancy/include) target_include_directories(test PUBLIC include vendor/stb vendor/cli-fancy/include)
target_link_libraries(test PUBLIC ctrff) 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 | | | 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 | Files are bit diffrent to the ones bannertool generates (don't know why) | | 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 |
| cbmd | Nothing done yet | | | cbmd | Nothing done yet | |
+2 -1
View File
@@ -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
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); 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
+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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
/*
#ifdef _WIN32 // Windows (MSVC Tested) #ifdef _WIN32 // Windows (MSVC Tested)
#ifdef CTRFF_BUILD_SHARED #ifdef CTRFF_BUILD_SHARED
#define CTRFF_API __declspec(dllexport) #define CTRFF_API __declspec(dllexport)
@@ -45,9 +45,9 @@ SOFTWARE.
#elif defined(__3DS__) // 3ds Specific #elif defined(__3DS__) // 3ds Specific
// Only Static supported // Only Static supported
#define CTRFF_API #define CTRFF_API
#else #else*/
#define CTRFF_API #define CTRFF_API
#endif //#endif
#include <cstddef> #include <cstddef>
#include <format> #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; 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));
+44 -32
View File
@@ -5,8 +5,8 @@
namespace ctrff { namespace ctrff {
namespace LZ11 { namespace LZ11 {
ctrff::u32 GetOccurenceLength(const ctrff::u8* new_ptr, size_t new_len, ctrff::u32 GetOccurenceLength(const ctrff::u8* new_ptr, u32 new_len,
const ctrff::u8* old_ptr, size_t old_len, const ctrff::u8* old_ptr, u32 old_len,
ctrff::u32& disp) { ctrff::u32& disp) {
disp = 0; disp = 0;
if (new_len == 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; ctrff::u32 res = 0;
if (old_len > 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; auto ref = old_ptr + i;
size_t len = 0; u32 len = 0;
for (size_t j = 0; j < new_len; j++) { for (u32 j = 0; j < new_len; j++) {
if (*(ref + j) != (*new_ptr + j)) { if (*(ref + j) != *(new_ptr + j)) {
break; break;
} }
len++; len++;
@@ -41,32 +41,41 @@ CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
return std::vector<ctrff::u8>(); return std::vector<ctrff::u8>();
} }
std::stringstream s; 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 // // SETUP HEADER //
s << static_cast<ctrff::u8>(0x11); s.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr));
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);
size_t res_len = 4; // 4-byte header u32 res_len = 4; // 4-byte header
ctrff::u8 out_buf[0x21]; // 33 bytes ctrff::u8 out_buf[0x21]; // 33 bytes
out_buf[0] = 0; memset(out_buf, 0x0, sizeof(out_buf));
size_t obl = 1; // out_buf_len u32 obl = 1; // out_buf_len
size_t buf_blocks = 0; u32 buf_blocks = 0;
size_t rb = 0; u32 rb = 0;
while (rb < in.size()) { 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); s.write(reinterpret_cast<const char*>(out_buf), obl);
res_len += obl; res_len += obl;
out_buf[0] = 0; memset(out_buf, 0x0, sizeof(out_buf));
obl = 1; obl = 1;
buf_blocks = 0; buf_blocks = 0;
} }
ctrff::u32 disp = 0; ctrff::u32 disp = 0;
size_t old_len = std::min(rb, static_cast<size_t>(0x1000)); u32 old_len = std::min(rb, static_cast<u32>(0x1000));
size_t len = LZ11::GetOccurenceLength( u32 len = LZ11::GetOccurenceLength(
in.data() + rb, std::min(in.size() - rb, static_cast<size_t>(0x10110)), in.data() + rb,
std::min((u32)in.size() - rb, static_cast<u32>(0x10110)),
in.data() + rb - old_len, old_len, disp); in.data() + rb - old_len, old_len, disp);
if (len < 3) { if (len < 3) {
@@ -75,20 +84,24 @@ CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
rb += len; rb += len;
out_buf[0] |= static_cast<ctrff::u8>(1 << (7 - buf_blocks)); out_buf[0] |= static_cast<ctrff::u8>(1 << (7 - buf_blocks));
if (len > 0x110) { if (len > 0x110) {
out_buf[obl++] = out_buf[obl] = 0x10;
0x10 | static_cast<ctrff::u8>(((len - 0x111) >> 12) & 0x0F); out_buf[obl] |= static_cast<ctrff::u8>(((len - 0x111) >> 12) & 0x0F);
out_buf[obl++] = static_cast<ctrff::u8>(((len - 0x111) >> 4) & 0xFF); obl++;
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x111) >> 4) & 0xFF);
obl++;
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x111) << 4) & 0xF0); out_buf[obl] = static_cast<ctrff::u8>(((len - 0x111) << 4) & 0xF0);
} else if (len > 0x10) { } else if (len > 0x10) {
out_buf[obl++] = out_buf[obl] = 0x00;
0x00 | static_cast<ctrff::u8>(((len - 0x11) >> 4) & 0x0F); out_buf[obl] |= static_cast<ctrff::u8>(((len - 0x111) >> 4) & 0x0F);
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x11) << 4) & 0xF0); obl++;
out_buf[obl] = static_cast<ctrff::u8>(((len - 0x111) << 4) & 0xF0);
} else { } 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++; obl++;
out_buf[obl++] = static_cast<ctrff::u8>(((disp - 1) >> 8) & 0x0F);
out_buf[obl++] = static_cast<ctrff::u8>((disp - 1) & 0xFF);
} }
buf_blocks++; buf_blocks++;
} }
@@ -105,9 +118,8 @@ CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
res_len += pad_len; res_len += pad_len;
} }
std::vector<ctrff::u8> res(res_len); std::string tmp = s.str();
s.read(reinterpret_cast<char*>(res.data()), res.size()); return std::vector<u8>(tmp.begin(), tmp.end());
return res;
} }
} // namespace LZ11 } // namespace LZ11
} // namespace ctrff } // namespace ctrff
+78 -2
View File
@@ -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,9 +443,76 @@ 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 = true; cf7::fancy_print = false;
cf7::colors_supported = true; cf7::colors_supported = false;
cf7::arg_mgr mgr(argc, argv); cf7::arg_mgr mgr(argc, argv);
mgr.SetAppInfo("ctrff", "1.0.0"); mgr.SetAppInfo("ctrff", "1.0.0");
auto makesmdh_cmd = cf7::command("makesmdh", "Create a SMDH File"); 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") 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;
} }
Vendored Submodule
+1
Submodule vendor/palladium added at 3575a6787d