Compare commits

13 Commits
Author SHA1 Message Date
tobid7 35fb691c37 Merge branch 'main' of github.com:tobid7/ctrff 2026-03-11 15:48:55 +01:00
tobid7 a000b46f7d Fix sign-compare issues
- Temp remove smdh size check (to fix usage with _3dsx)
2026-03-11 15:46:31 +01:00
tobid7 e75b9a627e Add alias target 2026-03-01 16:55:00 +01:00
tobid7 61061277f6 Update palaldium / add cmake install targets 2026-02-27 20:13:11 +01:00
tobid7 7bc7c60a15 Add Image RGBA Flip func 2026-01-14 18:25:05 +01:00
tobid7 888db35079 Move docs 2026-01-12 21:36:40 +01:00
tobid7 4d0f380d25 optimisation (crash fix) 2026-01-09 21:29:10 +01:00
tobid7 2025e78654 hotfix 2026-01-09 20:54:33 +01:00
tobid7 6ae295bcb1 Fix DLL building 2026-01-09 14:34:38 +01:00
tobid7 f5a58278db Add support to more color formats
- RGBA4444: fully working
- RGBA5551: fully not working
- LA8, LA4: fully working
2026-01-08 18:59:47 +01:00
tobid7 86461481ae No comemnt on this :/ 2026-01-07 18:42:16 +01:00
tobid7 022d69dc8e Add Pica Texture Encoding Decoding
Supported Formats: A4, A8, L4, L8, RGB565, RGB888 RGBA8888 (for now)
Add a .clang-format
Move ctrff into a subdirectory
Replace the BCLIM creation part with Pica encoder
Fix non const RGB565ToRGBA issue
2026-01-06 21:51:51 +01:00
tobid7 e4fa2202d2 Add LZ11 Decompression
- Add getetrs for bclim
- Correctly load all bclim data
- Fix RGB565ToRGBA converter to support all sizes possible
2026-01-03 01:54:30 +01:00
25 changed files with 921 additions and 440 deletions
+321
View File
@@ -0,0 +1,321 @@
---
Language: Cpp
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: true
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AllowShortNamespacesOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: BinPack
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: None
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakBinaryOperations: Never
BreakConstructorInitializers: BeforeColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakTemplateDeclarations: Yes
ColumnLimit: 80
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: "^<.*"
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: ".*"
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: "([-_](test|unittest))?$"
IncludeIsMainSourceRegex: ""
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExportBlock: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: false
AtStartOfFile: true
KeepFormFeed: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ""
MacroBlockEnd: ""
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakBeforeMemberAccess: 150
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Leave
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- "c++"
- "C++"
CanonicalDelimiter: ""
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: Always
RemoveBracesLLVM: false
RemoveEmptyLinesInUnwrappedLines: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TableGenBreakInsideDAGArg: DontBreak
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
WrapNamespaceBodyWithEmptyLines: Leave
...
+3 -2
View File
@@ -1,2 +1,3 @@
build/ build*/
.cache .cache
.zed
+58 -14
View File
@@ -1,16 +1,20 @@
cmake_minimum_required(VERSION 3.22) cmake_minimum_required(VERSION 3.22)
project(ctrff) project(ctrff VERSION 0.2.0)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED true) set(CMAKE_CXX_STANDARD_REQUIRED true)
option(CTRFF_DESKTOP OFF "Buid for Desktop Platform") option(CTRFF_DESKTOP OFF "Buid for Desktop Platform")
option(CTRFF_3DS OFF "Build lib for 3ds") option(CTRFF_3DS OFF "Build lib for 3ds")
option(CTRFF_SHARED OFF)
if(${CTRFF_3DS}) if(CTRFF_3DS)
set(CTRFF_DESKTOP OFF CACHE BOOL) set(CTRFF_DESKTOP OFF CACHE BOOL)
set(CTRFF_BUILD_GUI OFF CACHE BOOL) set(CTRFF_BUILD_GUI OFF CACHE BOOL)
set(CTRFF_SHARED OFF CACHE BOOL)
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
if(DEFINED ENV{DEVKITPRO}) if(DEFINED ENV{DEVKITPRO})
set(CMAKE_TOOLCHAIN_FILE "$ENV{DEVKITPRO}/cmake/3DS.cmake" CACHE PATH "toolchain file") set(CMAKE_TOOLCHAIN_FILE "$ENV{DEVKITPRO}/cmake/3DS.cmake" CACHE PATH "toolchain file")
@@ -21,7 +25,7 @@ if(${CTRFF_3DS})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-psabi -O3") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-psabi -O3")
endif() endif()
add_library(ctrff STATIC set(CTRFF_SOURCE
source/3dsx.cpp source/3dsx.cpp
source/bclim.cpp source/bclim.cpp
source/bcstm.cpp source/bcstm.cpp
@@ -29,19 +33,59 @@ add_library(ctrff STATIC
source/binutil.cpp source/binutil.cpp
source/helper.cpp source/helper.cpp
source/lz11.cpp source/lz11.cpp
source/pica.cpp
source/smdh.cpp source/smdh.cpp
) )
target_include_directories(ctrff PUBLIC include)
if(${CTRFF_DESKTOP}) if(CTRFF_SHARED)
add_subdirectory(vendor/palladium) add_library(ctrff SHARED ${CTRFF_SOURCE})
add_executable(ctrff-cli tool/main.cpp) target_compile_definitions(ctrff PUBLIC CTRFF_SHARED)
target_include_directories(ctrff-cli PUBLIC include vendor/stb vendor/cli-fancy/include) target_compile_definitions(ctrff PRIVATE CTRFF_BUILD_SHARED)
target_link_libraries(ctrff-cli PUBLIC ctrff palladium) else()
add_executable(test tool/test.cpp) add_library(ctrff STATIC ${CTRFF_SOURCE})
target_include_directories(test PUBLIC include vendor/stb vendor/cli-fancy/include)
target_link_libraries(test PUBLIC ctrff)
endif() endif()
install(TARGETS ctrff) target_include_directories(ctrff
install(DIRECTORY include DESTINATION ".") PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
add_library(ctrff::ctrff ALIAS ctrff)
if(CTRFF_DESKTOP)
add_subdirectory(tools)
endif()
install(TARGETS ctrff
EXPORT ctrffTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT ctrffTargets
FILE ctrffTargets.cmake
NAMESPACE ctrff::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ctrff
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
cmake/ctrffConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/ctrffConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ctrff
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/ctrffConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/ctrffConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/ctrffConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ctrff
)
+3 -3
View File
@@ -4,7 +4,7 @@ Tool/Library to work with Nintendo 3ds File formats
## Note ## Note
**CTRFF Requires Palladium Headers from branch devel040 (as this is the branch the lib was latest tested with)** The Docs can be found in the [Wiki](https://github.com/tobid7/ctrff/wiki) now.
## Building ## Building
@@ -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 | Creating A8,RGBA32,RGB565 done | WIP | | bclim | Creating A4,A8,LA4,LA8,L4,L8,RGB24,RGBA32,RGBA4444,RGB565 done | WIP |
| lz11 | Encoder done, Decoder missing | WIP | | lz11 | Encoder done, Decoder done | |
| 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 | |
+3
View File
@@ -0,0 +1,3 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/ctrffTargets.cmake")
-179
View File
@@ -1,179 +0,0 @@
# BCSTM File Format
**Note that this Docs are based on Current State of development and are very unfinished**
## Contents
- 1 [Header](#header)
- 2 [Reference](#reference)
- 2.1 [Sized Reference](#sized-reference)
- 2.2 [Reference Table](#reference-table)
- 2.3 [Reference Types](#reference-types)
- 3 [Block Header](#block-header)
- 4 [Info Block](#info-block)
- 4.1 [Stream Info](#stream-info)
- 4.2 [Channel Info](#channel-info)
- 5 [DSP ADPCM Info](#dsp-adpcm-info)
- 5.1 [DSP ADPCM Param](#dsp-adpcm-param)
- 5.2 [DSP ADPCM Context](#dsp-adpcm-context)
- 6 [Basic Data Types](#basic-datatypes)
- 7 [Tools Devices used for Research](#tools--devices--file-sources-used-for-research)
## Header
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 4 | [u32](#u32) | Magic **'CSTM'** `0x4D545343` |
| 0x04 | 2 | [u16](#u16) | Endianness `Big == 0xfffe` `Little == 0xfeff` |
| 0x06 | 2 | [u16](#u16) | Header Size `0x40` |
| 0x08 | 4 | [u32](#u32) | Version |
| 0x0c | 4 | [u32](#u32) | File Size |
| 0x10 | 2 | [u16](#u16) | Num Blocks (Should be 3) |
| 0x12 | 2 | [u16](#u16) | Reserved |
| 0x14 | 12 | [Sized Reference](#sized-reference) | Info Block Sized Reference |
| 0x20 | 12 | [Sized Reference](#sized-reference) | Seek Block Sized Reference |
| 0x2c | 12| [Sized Reference](#sized-reference) | Data Block Sized Reference |
## Reference
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 2 | [Reference Type](#reference-types) | TypeID |
| 0x02 | 2 | [u16](u16) | Padding |
| 0x04 | 4 | [u32](u32) | Offset **(0xffffffff represents null)** |
### Sized Reference
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 8 | [Reference](#reference) | Reference |
| 0x08 | 4 | [u32](u32) | Size |
### Reference Table
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 4 | [u32](#u32) | Count |
| 0x04 | Count*8 | [Reference](#reference) | References |
### Reference Types
| ID | Type |
|---|---|
| 0x0100 | Byte Table |
| 0x0101 | [Reference Table](#reference-table) |
| 0x0300 | [DSP ADPCM Info](#dsp-adpcm-info) |
| 0x0301 | IMA ADPCM Info |
| 0x1f00 | Sample Data |
| 0x4000 | [Info Block](#info-block) |
| 0x4001 | Seek Block |
| 0x4002 | Data Block |
| 0x4100 | [Stream Info](#stream-info) |
| 0x4101 | Track Info |
| 0x4102 | [Channel Info](#channel-info) |
## Block Header
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 4 | [u32](#u32) | Magic |
| 0x04 | 4 | [u32](#u32) | Size |
## Info Block
All Reference Offsets at the beginning of Info Block are Relative to the **Infoblock + 0x08 (size of Block Header) Position**
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 8 | [Block Header](#block-header) | Block Header |
| 0x08 | 8 | [Reference](#reference) | Stream Info Reference |
| 0x10 | 8 | [Reference](#reference) | Track Info Reference Table Reference |
| 0x18 | 8 | [Reference](#reference) | Channel Info Reference Table Table Reference |
| 0x20 | 56 | [Stream Info](#stream-info) | Stream Info |
### Stream Info
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 1 | [u8](#u8) | Encoding |
| 0x01 | 1 | [u8](#u8) | Loop `1 == true -- 0 == false` |
| 0x02 | 1 | [u8](#u8) | Num Channels |
| 0x03 | 1 | [u8](#u8) | Padding |
| 0x04 | 4 | [u32](#u32) | Sample Rate |
| 0x04 | 4 | [u32](#u32) | Loop Start |
| 0x04 | 4 | [u32](#u32) | Loop End |
| 0x04 | 4 | [u32](#u32) | Sample Blocks |
| 0x04 | 4 | [u32](#u32) | Sample Block Size |
| 0x04 | 4 | [u32](#u32) | Sample Block Samples |
| 0x04 | 4 | [u32](#u32) | Last Sample Block Size |
| 0x04 | 4 | [u32](#u32) | Last Sample Block Samples |
| 0x04 | 4 | [u32](#u32) | Last Sample Block Padded Size |
| 0x04 | 4 | [u32](#u32) | Seek Data Size |
| 0x04 | 4 | [u32](#u32) | Seek Interval Samples |
| 0x04 | 4 | [Reference](#reference) | Sample Data Reference |
### Channel Info
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 8 | [Reference](#reference) | Reference to [DSP ADPCM Info](#dsp-adpcm-info) **!!! The Offsets are Relative to the start Pos of the Channel Info Object !!!** |
## DSP ADPCM Info
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 32 | [Param](#dsp-adpcm-param) | Coefficients |
| 0x20 | 6 | [DSP Context](#dsp-adpcm-context) | Context |
| 0x26 | 6 | [DSP Context](#dsp-adpcm-context) | Loop Context |
| 0x2c | 2 | [u16](#u16) | Padding |
### DSP ADPCM Param
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 32 | [u16](#u16) | 16 Bit Coefficients |
### DSP ADPCM Context
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 1 | [u8](#u8) | 4Bit Predictor and 4Bit Scale |
| 0x01 | 1 | [u8](#u8) | Reserved |
| 0x02 | 2 | [16](#u16) | Previous Sample |
| 0x02 | 2 | [16](#u16) | Second Previous Sample |
## Basic Datatypes
### u32
```cpp
using u32 = unsigned int; // or uint32_t
```
### u16
```cpp
using u16 = unsigned short; // or uint16_t
```
### u8
```cpp
using u8 = unsigned char; // or uint8_t
```
## Tools / Devices / File Sources used for research
| Name | Description |
|---|---|
| Visual Studio Code | Used for creating ctrff c++ code for bcstm |
| ImHex | Used to Analyze the Hex Code of the bcstm Files |
| LoopingAudioConverter | Used to create some files for testing |
| Citra | Fast way to generate Log files when developing ctrff |
| ctrff-cli | Tool to generate Debug Output on Desktop OS like seen in BCSTM-Player File inspector |
| New 3ds XL | Testing on Real Hardware (BCSTM-Player) |
| Mario Kart 7 (Cartridge) | Used to get Test files |
| CTGP 7 | Used to get Test files |
| Super Mario Maker 3ds (Cartridge) | Used to get Test files |
| Mario and Luigi Bowsers inside story (Cartridge) | Used to get Test files |
| Donkey Kong Country Returns 3D (Cardridge) | Used to get Test files |
-157
View File
@@ -1,157 +0,0 @@
# BCWAV File Format
**Note that this Docs are based on Current State of development and are very unfinished**
**This file can be very incorect due to copying from bcstm_doku.md**
## Contents
- 1 [Header](#header)
- 2 [Reference](#reference)
- 2.1 [Sized Reference](#sized-reference)
- 2.2 [Reference Table](#reference-table)
- 2.3 [Reference Types](#reference-types)
- 3 [Block Header](#block-header)
- 4 [Info Block](#info-block)
- 4.1 [Channel Info](#channel-info)
- 5 [DSP ADPCM Info](#dsp-adpcm-info)
- 5.1 [DSP ADPCM Param](#dsp-adpcm-param)
- 5.2 [DSP ADPCM Context](#dsp-adpcm-context)
- 6 [Basic Data Types](#basic-datatypes)
- 7 [Tools Devices used for Research](#tools--devices--file-sources-used-for-research)
## Header
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 4 | [u32](#u32) | Magic **'CWAV'** `0x56415743` |
| 0x04 | 2 | [u16](#u16) | Endianness `Big == 0xfffe` `Little == 0xfeff` |
| 0x06 | 2 | [u16](#u16) | Header Size `0x40` |
| 0x08 | 4 | [u32](#u32) | Version |
| 0x0c | 4 | [u32](#u32) | File Size |
| 0x10 | 2 | [u16](#u16) | Num Blocks (Should be 2) |
| 0x12 | 2 | [u16](#u16) | Reserved |
| 0x14 | 12 | [Sized Reference](#sized-reference) | Info Block Sized Reference |
| 0x20 | 12| [Sized Reference](#sized-reference) | Data Block Sized Reference |
## Reference
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 2 | [Reference Type](#reference-types) | TypeID |
| 0x02 | 2 | [u16](u16) | Padding |
| 0x04 | 4 | [u32](u32) | Offset **(0xffffffff represents null)** |
### Sized Reference
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 8 | [Reference](#reference) | Reference |
| 0x08 | 4 | [u32](u32) | Size |
### Reference Table
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 4 | [u32](#u32) | Count |
| 0x04 | Count*8 | [Reference](#reference) | References |
### Reference Types
| ID | Type |
|---|---|
| 0x0300 | [DSP ADPCM Info](#dsp-adpcm-info) |
| 0x0301 | IMA ADPCM Info |
| 0x1f00 | Sample Data |
| 0x7000 | [Info Block](#info-block) |
| 0x7001 | Data Block |
| 0x7100 | [Channel Info](#channel-info) |
## Block Header
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 4 | [u32](#u32) | Magic |
| 0x04 | 4 | [u32](#u32) | Size |
## Info Block
All Reference Offsets at the beginning of Info Block are Relative to the **Infoblock + 0x08 (size of Block Header) Position**
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 8 | [Block Header](#block-header) | Block Header |
| 0x08 | 1 | [u8](#u8) | Encoding |
| 0x09 | 1 | [u8](#u8) | Loop `1 == true -- 0 == false` |
| 0x0a | 1 | [u16](#u16) | Padding |
| 0x0c | 4 | [u32](#u32) | Sample Rate |
| 0x10 | 4 | [u32](#u32) | Loop Start |
| 0x14 | 4 | [u32](#u32) | Loop End |
| 0x18 | 4 | [u32](#u32) | Reserved |
| 0x18 | 8 | [Reference Table](#reference-table) | Channel Info Reference Table |
### Channel Info
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 8 | [Reference](#reference) | Reference to [DSP ADPCM Info](#dsp-adpcm-info) **!!! The Offsets are Relative to the start Pos of the Channel Info Object !!!** |
## DSP ADPCM Info
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 32 | [Param](#dsp-adpcm-param) | Coefficients |
| 0x20 | 6 | [DSP Context](#dsp-adpcm-context) | Context |
| 0x26 | 6 | [DSP Context](#dsp-adpcm-context) | Loop Context |
| 0x2c | 2 | [u16](#u16) | Padding |
### DSP ADPCM Param
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 32 | [u16](#u16) | 16 Bit Coefficients |
### DSP ADPCM Context
| Offset | Size | Datatype | Description |
|---|---|---|---|
| 0x00 | 1 | [u8](#u8) | 4Bit Predictor and 4Bit Scale |
| 0x01 | 1 | [u8](#u8) | Reserved |
| 0x02 | 2 | [16](#u16) | Previous Sample |
| 0x02 | 2 | [16](#u16) | Second Previous Sample |
## Basic Datatypes
### u32
```cpp
using u32 = unsigned int; // or uint32_t
```
### u16
```cpp
using u16 = unsigned short; // or uint16_t
```
### u8
```cpp
using u8 = unsigned char; // or uint8_t
```
## Tools / Devices / File Sources used for research
| Name | Description |
|---|---|
| Visual Studio Code | Used for creating ctrff c++ code for bcstm |
| ImHex | Used to Analyze the Hex Code of the bcstm Files |
| Citra | Fast way to generate Log files when developing ctrff |
| LoopingAudioConverter | Used to create some files for testing |
| ctrff-cli | Tool to generate Debug Output on Desktop OS like seen in BCSTM-Player File inspector |
| New 3ds XL | Testing on Real Hardware (BCSTM-Player) |
| Mario Kart 7 (Cartridge) | Used to get Test files |
| CTGP 7 | Used to get Test files |
| Super Mario Maker 3ds (Cartridge) | Used to get Test files |
| Mario and Luigi Bowsers inside story (Cartridge) | Used to get Test files |
| Donkey Kong Country Returns 3D (Cardridge) | Used to get Test files |
+1
View File
@@ -6,4 +6,5 @@
#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/pica.hpp>
#include <ctrff/smdh.hpp> #include <ctrff/smdh.hpp>
+1 -1
View File
@@ -22,7 +22,7 @@ class CTRFF_API _3dsx : public BinFile {
void Write(std::fstream& f) const override; void Write(std::fstream& f) const override;
void Read(std::fstream& f) override; void Read(std::fstream& f) override;
ctrff::u32 Magic; ctrff::u32 Magic; // 0x58534433 "3DSX"
ctrff::u16 HeaderSize; ctrff::u16 HeaderSize;
ctrff::u16 RelocHeaderSize; ctrff::u16 RelocHeaderSize;
ctrff::u32 FormatVersion; ctrff::u32 FormatVersion;
+14 -9
View File
@@ -9,24 +9,24 @@
namespace ctrff { namespace ctrff {
class CTRFF_API BCLIM : public BinFile { class CTRFF_API BCLIM : public BinFile {
public: public:
BCLIM() {} BCLIM() : pCurrent(Header::Default()), pImag(ImagHeader::Default()) {}
~BCLIM() {} ~BCLIM() {}
enum Format : u32 { enum Format : u32 {
L8, L8, // tested
A8, // tested A8, // tested
LA4, LA4, // tested
LA8, LA8, // tested
HILO8, HILO8,
RGB565, // tested RGB565, // tested
RGB888, RGB888, // tested
RGBA5551, RGBA5551,
RGBA4444, RGBA4444, // tested
RGBA8888, // tested RGBA8888, // tested
ETC1, ETC1,
ETC1A4, ETC1A4,
L4, L4, // tested
A4, A4, // tested
}; };
struct Header { struct Header {
@@ -81,6 +81,11 @@ class CTRFF_API BCLIM : public BinFile {
void CreateByImage(const std::vector<u8>& data, int w, int h, Format fmt); void CreateByImage(const std::vector<u8>& data, int w, int h, Format fmt);
Format GetFmt() const { return (Format)pImag.Format; }
std::vector<u8> GetImage() { return pBuffer; }
int GetWidth() const { return pImag.Width; }
int GetHeight() const { return pImag.Height; }
/** Write not supported btw */ /** Write not supported btw */
void Write(std::fstream& f) const override; void Write(std::fstream& f) const override;
void Read(std::fstream& f) override; void Read(std::fstream& f) override;
+1 -1
View File
@@ -92,7 +92,7 @@ class CTRFF_API BCSTM {
ctrff::u32 Size; ctrff::u32 Size;
}; };
struct StreamInfo { struct CTRFF_API StreamInfo {
StreamInfo(); StreamInfo();
ctrff::u8 Encoding; ctrff::u8 Encoding;
ctrff::u8 Loop; ctrff::u8 Loop;
+2 -1
View File
@@ -5,12 +5,13 @@
namespace ctrff { namespace ctrff {
CTRFF_API void String2U16(ctrff::u16 *res, const std::string &src, size_t max); CTRFF_API void String2U16(ctrff::u16 *res, const std::string &src, size_t max);
CTRFF_API std::string U16toU8(ctrff::u16 *in, size_t max); CTRFF_API std::string U16toU8(ctrff::u16 *in, size_t max);
CTRFF_API void RGB565toRGBA(std::vector<ctrff::u8> &img, ctrff::u16 *icon, CTRFF_API void RGB565toRGBA(std::vector<ctrff::u8> &img, const ctrff::u16 *icon,
const int &w, const int &h); const int &w, const int &h);
// Image can only be rgba8888 // Image can only be rgba8888
CTRFF_API void RGBA2RGB565(ctrff::u16 *out, const std::vector<ctrff::u8> &img, 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 void Flip(std::vector<u8> &buf, int w, int h);
CTRFF_API ctrff::u32 TileIndex(const int &x, const int &y, const int &w); CTRFF_API ctrff::u32 TileIndex(const int &x, const int &y, const int &w);
} // namespace ctrff } // namespace ctrff
+2 -1
View File
@@ -5,5 +5,6 @@
namespace ctrff { namespace ctrff {
namespace LZ11 { namespace LZ11 {
CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in); CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in);
} CTRFF_API std::vector<ctrff::u8> Decompress(const std::vector<ctrff::u8>& in);
} // namespace LZ11
} // namespace ctrff } // namespace ctrff
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <ctrff/types.hpp>
/**
* 3ds GPU Stuff
*/
namespace ctrff {
namespace Pica {
enum Color : u32 {
L8, // tested
A8, // tested
LA4, // tested
LA8, // tested
HILO8,
RGB565, // tested
RGB888, // tested
RGBA5551,
RGBA4444, // tested
RGBA8888, // tested
ETC1,
ETC1A4,
L4, // tested
A4, // tested
};
CTRFF_API void EncodeImage(std::vector<ctrff::u8>& ret,
const std::vector<ctrff::u8>& rgba, int w, int h,
Color dst);
CTRFF_API void DecodeImage(std::vector<ctrff::u8>& ret,
const std::vector<ctrff::u8>& pixels, int w, int h,
Color src);
} // namespace Pica
} // namespace ctrff
+8 -3
View File
@@ -23,7 +23,8 @@ 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 CTRFF_SHARED
#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,15 +46,19 @@ 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
#else
#define CTRFF_API
#endif
#include <cstddef> #include <cstddef>
#include <format> #include <format>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <numeric>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
+9 -8
View File
@@ -18,22 +18,23 @@ CTRFF_API void BCLIM::Write(std::fstream& f) const {
} }
CTRFF_API void BCLIM::Read(std::fstream& f) { CTRFF_API void BCLIM::Read(std::fstream& f) {
f.seekg(std::ios::end); f.seekg(0, std::ios::end);
size_t size = f.tellg(); size_t size = f.tellg();
if (size < (sizeof(Header) + sizeof(ImagHeader))) { if (size < (sizeof(Header) + sizeof(ImagHeader))) {
throw std::runtime_error("Invalid File!"); throw std::runtime_error("Invalid File!");
} }
f.seekg(size - sizeof(Header) - sizeof(ImagHeader)); f.seekg(size - sizeof(Header) - sizeof(ImagHeader));
Header h; f.read(reinterpret_cast<char*>(&pCurrent), sizeof(pCurrent));
ImagHeader imag; f.read(reinterpret_cast<char*>(&pImag), sizeof(pImag));
f.read(reinterpret_cast<char*>(&h), sizeof(h)); if (pCurrent.Magic != 0x4d494c43) {
f.read(reinterpret_cast<char*>(&imag), sizeof(imag));
if (h.Magic != 0x4d494c43) {
throw std::runtime_error("[ctrff] BCLIM: Not a bclim file!"); throw std::runtime_error("[ctrff] BCLIM: Not a bclim file!");
} }
if (imag.Magic != 0x67616d69) { if (pImag.Magic != 0x67616d69) {
throw std::runtime_error("[ctrff] BCLIM: Invalid Data"); throw std::runtime_error("[ctrff] BCLIM: Invalid Data");
} }
f.seekg(0, std::ios::beg);
pBuffer.resize(pImag.ImageSize);
f.read(reinterpret_cast<char*>(pBuffer.data()), pBuffer.size());
} }
CTRFF_API void BCLIM::CreateByImage(const std::vector<u8>& data, int w, int h, CTRFF_API void BCLIM::CreateByImage(const std::vector<u8>& data, int w, int h,
@@ -46,7 +47,7 @@ CTRFF_API void BCLIM::CreateByImage(const std::vector<u8>& data, int w, int h,
pImag.Height = h; pImag.Height = h;
pImag.ImageSize = data.size(); pImag.ImageSize = data.size();
pBuffer.resize(data.size()); pBuffer.resize(data.size());
for (int i = 0; i < data.size(); i++) { for (size_t i = 0; i < data.size(); i++) {
pBuffer[i] = data[i]; pBuffer[i] = data[i];
} }
pCurrent.FileSize = pBuffer.size() + pCurrent.HeaderSize + pImag.HeaderSize; pCurrent.FileSize = pBuffer.size() + pCurrent.HeaderSize + pImag.HeaderSize;
+18 -4
View File
@@ -28,11 +28,11 @@ CTRFF_API ctrff::u32 ctrff::TileIndex(const int &x, const int &y,
// TODO: Fix colors // TODO: Fix colors
CTRFF_API void ctrff::RGB565toRGBA(std::vector<ctrff::u8> &img, CTRFF_API void ctrff::RGB565toRGBA(std::vector<ctrff::u8> &img,
ctrff::u16 *icon, const int &w, const ctrff::u16 *icon, const int &w,
const int &h) { const int &h) {
if (img.size() != (48 * 48 * 4)) { if (img.size() != size_t(w * h * 4)) {
img.resize(48 * 48 * 4);
img.clear(); img.clear();
img.resize(w * h * 4);
} }
for (int y = 0; y < h; y++) { for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) { for (int x = 0; x < w; x++) {
@@ -87,6 +87,20 @@ CTRFF_API std::vector<ctrff::u8> ctrff::DownscaleImage(
return res; return res;
} }
CTRFF_API void ctrff::Flip(std::vector<ctrff::u8> &buf, int w, int h) {
std::vector<ctrff::u8> tmp = buf;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4;
int dst = ((h - 1 - y) * w + x) * 4;
buf[dst + 0] = tmp[src + 0];
buf[dst + 1] = tmp[src + 1];
buf[dst + 2] = tmp[src + 2];
buf[dst + 3] = tmp[src + 3];
}
}
}
CTRFF_API void ctrff::String2U16(ctrff::u16 *res, const std::string &src, CTRFF_API void ctrff::String2U16(ctrff::u16 *res, const std::string &src,
size_t max) { size_t max) {
/// GOT FORCED TO REPLACE std::wstring_convert by some /// GOT FORCED TO REPLACE std::wstring_convert by some
@@ -150,7 +164,7 @@ CTRFF_API std::string ctrff::U16toU8(ctrff::u16 *in, size_t max) {
} else if (c < 0x800) { } else if (c < 0x800) {
result.push_back(static_cast<char>(0xC0 | (c >> 6))); result.push_back(static_cast<char>(0xC0 | (c >> 6)));
result.push_back(static_cast<char>(0x80 | (c & 0x3F))); result.push_back(static_cast<char>(0x80 | (c & 0x3F)));
} else if (c < 0x10000) { } else if (c <= 0xffff) {
result.push_back(static_cast<char>(0xE0 | (c >> 12))); result.push_back(static_cast<char>(0xE0 | (c >> 12)));
result.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3F))); result.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3F)));
result.push_back(static_cast<char>(0x80 | (c & 0x3F))); result.push_back(static_cast<char>(0x80 | (c & 0x3F)));
+51 -1
View File
@@ -37,7 +37,7 @@ ctrff::u32 GetOccurenceLength(const ctrff::u8* new_ptr, u32 new_len,
CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) { CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
if (in.size() > 0xFFFFFF) { if (in.size() > 0xFFFFFF) {
std::cout << "ERROR: LZ11 input is too large!" << std::endl; throw std::runtime_error("[ctrff] LZ11: Input is tool large to compress");
return std::vector<ctrff::u8>(); return std::vector<ctrff::u8>();
} }
std::stringstream s; std::stringstream s;
@@ -121,5 +121,55 @@ CTRFF_API std::vector<ctrff::u8> Compress(const std::vector<ctrff::u8>& in) {
std::string tmp = s.str(); std::string tmp = s.str();
return std::vector<u8>(tmp.begin(), tmp.end()); return std::vector<u8>(tmp.begin(), tmp.end());
} }
// Baed on
// https://github.com/Gericom/EveryFileExplorer/blob/master/CommonCompressors/LZ11.cs
// Needs some fixes cause this code looks very unsafe to me tbh
CTRFF_API std::vector<ctrff::u8> Decompress(const std::vector<ctrff::u8>& in) {
if (!in.size()) {
throw std::runtime_error("[ctrff] LZ11: Cannot decompress empty buffer!");
}
if (in[0] != 0x11) {
throw std::runtime_error("[ctrff] LZ11: Not a lz11 file!");
}
u32 len = in[1] | (in[2] << 8) | (in[3] << 16);
std::vector<u8> ret(len, 0x0);
int off = 4;
int dst_off = 0;
while (true) {
u8 header = in[off++];
for (int i = 0; i < 8; i++) {
if ((header & 0x80) == 0) {
ret[dst_off++] = in[off++];
} else {
u8 a = in[off++];
int off2;
int length;
if ((a >> 4) == 0) {
u8 b = in[off++];
u8 c = in[off++];
length = (((a & 0xF) << 4) | (b >> 4)) + 0x11;
off2 = (((b & 0xF) << 8) | c) + 1;
} else if ((a >> 4) == 1) {
u8 b = in[off++];
u8 c = in[off++];
u8 d = in[off++];
length = (((a & 0xF) << 12) | (b << 4) | (c >> 4)) + 0x111;
off2 = (((c & 0xF) << 8) | d) + 1;
} else {
u8 b = in[off++];
off2 = (((a & 0xF) << 8) | b) + 1;
length = (a >> 4) + 1;
}
for (int j = 0; j < length; j++) {
ret[dst_off] = ret[dst_off - off2];
dst_off++;
}
}
if ((u32)dst_off >= len) return ret;
header <<= 1;
}
}
}
} // namespace LZ11 } // namespace LZ11
} // namespace ctrff } // namespace ctrff
+311
View File
@@ -0,0 +1,311 @@
#include <ctrff/helper.hpp>
#include <ctrff/pica.hpp>
namespace ctrff {
namespace Pica {
CTRFF_API void EncodeImage(std::vector<ctrff::u8>& ret,
const std::vector<ctrff::u8>& rgba, int w, int h,
Color dst_color) {
// Only used in rgb/rgba
int bpp = dst_color == RGBA8888 ? 4 : 3;
switch (dst_color) {
case RGB565:
ret.resize(w * h * 2);
ctrff::RGBA2RGB565(reinterpret_cast<ctrff::u16*>(ret.data()), rgba, w, h);
break;
case RGB888:
case RGBA8888:
ret.resize(w * h * bpp);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w) * bpp;
for (int i = 0; i < bpp; i++) {
ret[dst + bpp - 1 - i] = rgba[src + i];
}
}
}
break;
case A8:
ret.resize(w * h);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w);
ret[dst] = rgba[src + 3]; // extract alpha only
}
}
break;
case A4:
ret.resize((w * h) >> 1);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w);
ctrff::u8 tmp = rgba[src + 3] >> 4;
int a4pos = dst >> 1;
if ((dst & 1) == 0) {
ret[a4pos] = (tmp << 4) | (ret[a4pos] & 0x0f);
} else {
ret[a4pos] = (ret[a4pos] & 0xf0) | tmp;
}
}
}
break;
case L8:
ret.resize(w * h);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w);
// Basic luminance calculation (already used in renderd7)
ret[dst] =
(rgba[src + 0] * 77 + rgba[src + 1] * 150 + rgba[src + 2] * 29) >>
8;
}
}
break;
case L4:
ret.resize((w * h) >> 1);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w);
// Basically same as a4 just with the luminance calculation func
ctrff::u8 tmp = ((rgba[src + 0] * 77 + rgba[src + 1] * 150 +
rgba[src + 2] * 29) >>
8) >>
4;
int a4pos = dst >> 1;
if ((dst & 1) == 0) {
ret[a4pos] = (tmp << 4) | (ret[a4pos] & 0x0f);
} else {
ret[a4pos] = (ret[a4pos] & 0xf0) | tmp;
}
}
}
break;
case RGBA4444:
ret.resize(w * h * 2);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w) * 2;
ret[dst + 0] = rgba[src + 3] >> 4;
ret[dst + 0] |= (rgba[src + 2] >> 4) << 4;
ret[dst + 1] = rgba[src + 1] >> 4;
ret[dst + 1] |= (rgba[src + 0] >> 4) << 4;
}
}
break;
case RGBA5551: // not working ???
ret.resize(w * h * 2);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w) * 2;
ctrff::u16 px = 0;
px |= (rgba[src + 3] < 128 ? 0 : 1) << 15; // A
px |= ((rgba[src + 2] >> 3) & 0x1f) << 10; // BBBBB
px |= ((rgba[src + 1] >> 3) & 0x1f) << 5; // GGGGG
px |= ((rgba[src + 0] >> 3) & 0x1f); // RRRRR
ret[dst] = px & 0xff;
ret[dst + 1] = (px << 8) & 0xff;
}
}
break;
case LA8:
ret.resize(w * h * 2);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w) * 2;
ret[dst] =
(rgba[src + 0] * 77 + rgba[src + 1] * 150 + rgba[src + 2] * 29) >>
8;
ret[dst + 1] = rgba[src + 3];
}
}
break;
case LA4:
ret.resize(w * h);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = (y * w + x) * 4; // basic rgba indexing btw
int dst = ctrff::TileIndex(x, y, w);
ret[dst] = 0;
ret[dst] |= (((rgba[src + 0] * 77 + rgba[src + 1] * 150 +
rgba[src + 2] * 29) >>
8) >>
4) &
0xf;
ret[dst] |= (((rgba[src + 3] >> 4) & 0xf) << 4) | ret[dst];
}
}
break;
default:
throw std::runtime_error("[ctrff] Pica: Unsupported Color format: " +
std::to_string((int)dst_color));
break;
}
}
CTRFF_API void DecodeImage(std::vector<ctrff::u8>& ret,
const std::vector<ctrff::u8>& pixels, int w, int h,
Color src_color) {
switch (src_color) {
case RGB565:
ret.resize(w * h * 4);
ctrff::RGB565toRGBA(
ret, reinterpret_cast<const ctrff::u16*>(pixels.data()), w, h);
break;
case RGB888:
case RGBA8888:
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int bpp = src_color == RGBA8888 ? 4 : 3;
int src = ctrff::TileIndex(x, y, w) * bpp;
int dst = (y * w + x) * 4; // basic rgba indexing btw
for (int i = 0; i < bpp; i++) {
ret[dst + i] = pixels[src + bpp - 1 - i];
}
if (src_color == RGB888) {
ret[dst + 3] = 255;
}
}
}
break;
case A8:
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w);
int dst = (y * w + x) * 4;
ret[dst + 0] = 255;
ret[dst + 1] = 255;
ret[dst + 2] = 255;
ret[dst + 3] = pixels[src];
}
}
break;
case A4: // most hated by me (tobid7)
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w);
int dst = (y * w + x) * 4;
/**
* Basically checking by %2 aka &1 if we have a high or low nibble
* by this we either extract the high or low one and multiply
* by 17 aka 0x11 to get the resulting A8 Value
*/
ctrff::u8 a4 = src & 1 ? ((pixels[src >> 1] >> 4) & 0xf) * 0x11
: (pixels[src >> 1] & 0xf) * 0x11;
ret[dst + 0] = 255;
ret[dst + 1] = 255;
ret[dst + 2] = 255;
ret[dst + 3] = a4;
}
}
break;
case L4: // most hated by me (tobid7)
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w);
int dst = (y * w + x) * 4;
/**
* Same as A4
* Basically checking by %2 aka &1 if we have a high or low nibble
* by this we either extract the high or low one and multiply
* by 17 aka 0x11 to get the resulting A8 Value
*/
ctrff::u8 a4 = src & 1 ? ((pixels[src >> 1] >> 4) & 0xf) * 0x11
: (pixels[src >> 1] & 0xf) * 0x11;
ret[dst + 0] = a4;
ret[dst + 1] = a4;
ret[dst + 2] = a4;
ret[dst + 3] = 255;
}
}
break;
case L8:
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w);
int dst = (y * w + x) * 4;
ret[dst + 0] = pixels[src];
ret[dst + 1] = pixels[src];
ret[dst + 2] = pixels[src];
ret[dst + 3] = 255;
}
}
break;
case RGBA4444:
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w) * 2;
int dst = (y * w + x) * 4; // basic rgba indexing btw
ret[dst + 0] = ((pixels[src + 1] >> 4) & 0xf) << 4;
ret[dst + 1] = (pixels[src + 1] & 0xf) << 4;
ret[dst + 2] = ((pixels[src + 0] >> 4) & 0xf) << 4;
ret[dst + 3] = (pixels[src + 0] & 0xf) << 4;
}
}
break;
case RGBA5551: // not working
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w) * 2;
int dst = (y * w + x) * 4; // basic rgba indexing btw
ctrff::u16 px = ret[src] | (ret[src + 1] << 8);
ret[dst + 0] = (px & 0x1f) * 0x1f;
ret[dst + 1] = ((px >> 5) & 0x1f) * 0x1f;
ret[dst + 2] = ((px >> 10) & 0x1f) * 0x1f;
ret[dst + 3] = 255; // ((*px >> 15) & 0x1) ? 255 : 0;
}
}
break;
case LA8:
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w) * 2;
int dst = (y * w + x) * 4;
ret[dst + 0] = pixels[src];
ret[dst + 1] = pixels[src];
ret[dst + 2] = pixels[src];
ret[dst + 3] = pixels[src + 1];
}
}
break;
case LA4:
ret.resize(w * h * 4);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int src = ctrff::TileIndex(x, y, w);
int dst = (y * w + x) * 4;
uint8_t l = (pixels[src] & 0xf) * 0x11;
uint8_t a = ((pixels[src] >> 4) & 0xf) * 0x11;
ret[dst + 0] = l;
ret[dst + 1] = l;
ret[dst + 2] = l;
ret[dst + 3] = a;
}
}
break;
default:
throw std::runtime_error("[ctrff] Pica: Unsupported Color format: " +
std::to_string((int)src_color));
break;
}
}
} // namespace Pica
} // namespace ctrff
+2 -2
View File
@@ -18,12 +18,12 @@ CTRFF_API void ctrff::SMDH::Write(std::fstream &f) const {
} }
CTRFF_API void ctrff::SMDH::Read(std::fstream &f) { CTRFF_API void ctrff::SMDH::Read(std::fstream &f) {
f.seekg(0, std::ios::end); /*f.seekg(0, std::ios::end);
if (f.tellg() != SMDH_Size) { if (f.tellg() != SMDH_Size) {
throw std::runtime_error( throw std::runtime_error(
"SMDH: File size does not match the SMDH Header size!"); "SMDH: File size does not match the SMDH Header size!");
} }
f.seekg(0, std::ios::beg); f.seekg(0, std::ios::beg);*/
f.read(reinterpret_cast<char *>(Magic), sizeof(Magic)); f.read(reinterpret_cast<char *>(Magic), sizeof(Magic));
if (std::string(Magic) != smdh_magic) { if (std::string(Magic) != smdh_magic) {
throw std::runtime_error("SMDH: Invalid SMDH file!"); throw std::runtime_error("SMDH: Invalid SMDH file!");
+5
View File
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.22)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../vendor/palladium palladium)
add_subdirectory(ctrff)
+25
View File
@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.22)
project(ctrff-cli)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED true)
add_executable(ctrff-cli source/main.cpp)
target_include_directories(ctrff-cli PUBLIC include
${CMAKE_CURRENT_SOURCE_DIR}/../../vendor/stb
${CMAKE_CURRENT_SOURCE_DIR}/../../vendor/cli-fancy/include
)
target_link_libraries(ctrff-cli PUBLIC ctrff palladium)
add_executable(test source/test.cpp)
target_include_directories(test PUBLIC include
${CMAKE_CURRENT_SOURCE_DIR}/../../vendor/stb
${CMAKE_CURRENT_SOURCE_DIR}/../../vendor/cli-fancy/include
)
target_link_libraries(test PUBLIC ctrff)
set_target_properties(ctrff-cli PROPERTIES
OUTPUT_NAME ctrff
)
install(TARGETS ctrff-cli)
+48 -53
View File
@@ -10,8 +10,7 @@
#include <utility> #include <utility>
/** Import palladium stb image */ /** Import palladium stb image */
#define STB_IMAGE_IMPLEMENTATION #include <pd/external/stb_image.hpp>
#include <pd/external/stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h> #include <stb_image_write.h>
/** Could not use the palladium func due to only using headers */ /** Could not use the palladium func due to only using headers */
@@ -122,7 +121,7 @@ void MakeSMDH(const cf7::command::ArgumentList &data) {
smdh.SetAuthor(a); smdh.SetAuthor(a);
std::vector<unsigned char> img; std::vector<unsigned char> img;
int w, h, c; int w, h, c;
ctrff::u8 *buf = stbi_load(i.c_str(), &w, &h, &c, 4); ctrff::u8 *buf = pdi_load(i.c_str(), &w, &h, &c, 4);
if (buf == nullptr) { if (buf == nullptr) {
cf7::PrintFancy({ cf7::PrintFancy({
std::make_pair("Error", cf7::col(190, 0, 0)), std::make_pair("Error", cf7::col(190, 0, 0)),
@@ -432,7 +431,12 @@ void LZ11Compress(const cf7::command::ArgumentList &data) {
std::make_pair(i, cf7::col(255, 210, 0)), std::make_pair(i, cf7::col(255, 210, 0)),
std::make_pair(FormatBytes(buf.size()), cf7::col(255, 255, 0)), std::make_pair(FormatBytes(buf.size()), cf7::col(255, 255, 0)),
}); });
auto res = ctrff::LZ11::Compress(buf); std::vector<ctrff::u8> res;
if (buf[0] == 0x11) {
res = ctrff::LZ11::Decompress(buf);
} else {
res = ctrff::LZ11::Compress(buf);
}
cf7::PrintFancy({ cf7::PrintFancy({
std::make_pair("Output", cf7::col(255, 165, 0)), std::make_pair("Output", cf7::col(255, 165, 0)),
std::make_pair(o, cf7::col(255, 210, 0)), std::make_pair(o, cf7::col(255, 210, 0)),
@@ -451,61 +455,49 @@ void BCLIMMaker(const cf7::command::ArgumentList &data) {
std::cout << "[ctrff] BCLIM: Error, no input or output" << std::endl; std::cout << "[ctrff] BCLIM: Error, no input or output" << std::endl;
return; return;
} }
if (f.empty() || (f.compare("rgba32") != 0 && f.compare("a8") != 0 && PD::Image::Ref img = PD::Image::New();
f.compare("rgb565") != 0)) { img->Load(i);
f = "a8"; if (img->GetBuffer().empty()) {
}
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; std::cout << "[ctrff] BCLIM: Failed to load image " + i << std::endl;
return; return;
} }
if (!PD::BitUtil::IsSingleBit(w) || !PD::BitUtil::IsSingleBit(h)) { if (!PD::BitUtil::IsSingleBit(img->Width()) ||
!PD::BitUtil::IsSingleBit(img->Height())) {
std::cout << "[ctrff] BCLIM: Image with and height must be a power of 8!"; std::cout << "[ctrff] BCLIM: Image with and height must be a power of 8!";
return; return;
} }
size_t size = w * h; img->Convert(img, img->RGBA);
if (f == "rgba32") { std::vector<ctrff::u8> res;
size *= 4; ctrff::Pica::Color fmt = ctrff::Pica::A8;
if (f == "a4") {
fmt = ctrff::Pica::A4;
} else if (f == "l4") {
fmt = ctrff::Pica::L4;
} else if (f == "a8") {
fmt = ctrff::Pica::A8;
} else if (f == "l8") {
fmt = ctrff::Pica::L8;
} else if (f == "la4") {
fmt = ctrff::Pica::LA4;
} else if (f == "la8") {
fmt = ctrff::Pica::LA8;
} else if (f == "rgb565") { } else if (f == "rgb565") {
size *= 2; fmt = ctrff::Pica::RGB565;
} } else if (f == "rgb888") {
std::vector<ctrff::u8> res(size); fmt = ctrff::Pica::RGB888;
if (f == "rgba32" || f == "a8") { } else if (f == "rgba4444") {
for (int x = 0; x < w; x++) { fmt = ctrff::Pica::RGBA4444;
for (int y = 0; y < h; y++) { } else if (f == "rgba5551") {
int src = (y * w + x) * 4; fmt = ctrff::Pica::RGBA5551;
int dst = ctrff::TileIndex(x, y, w); } else if (f == "rgba8888") {
if (f == "rgba32") { fmt = ctrff::Pica::RGBA8888;
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::Pica::EncodeImage(res, img->GetBuffer(), img->Width(), img->Height(),
fmt);
ctrff::BCLIM file; ctrff::BCLIM file;
ctrff::BCLIM::Format fmt = ctrff::BCLIM::A8; ctrff::BCLIM::Format _fmt = (ctrff::BCLIM::Format)fmt;
if (f == "rgba32") { file.CreateByImage(res, img->Width(), img->Height(), _fmt);
fmt = ctrff::BCLIM::RGBA8888;
} else if (f == "rgb565") {
fmt = ctrff::BCLIM::RGB565;
}
file.CreateByImage(res, w, h, fmt);
file.Save(o); file.Save(o);
std::cout << "File " + o + " created" << std::endl; std::cout << "File " + o + " created" << std::endl;
} }
@@ -556,7 +548,7 @@ int main(int argc, char *argv[]) {
"Output icon path!", false)) "Output icon path!", false))
.SetFunction(Read3DSX)); .SetFunction(Read3DSX));
mgr.AddCommand( mgr.AddCommand(
cf7::command("lz11", "Creates a LZ11 Compressed File") cf7::command("lz11", "Compress/Decompress a file with LZ11")
.AddSubEntry(cf7::command::sub("i", "input", "Input file path", true)) .AddSubEntry(cf7::command::sub("i", "input", "Input file path", true))
.AddSubEntry( .AddSubEntry(
cf7::command::sub("o", "output", "Output file path", true)) cf7::command::sub("o", "output", "Output file path", true))
@@ -570,8 +562,11 @@ int main(int argc, char *argv[]) {
.AddSubEntry(cf7::command::sub("i", "input", "Input png|bmp", true)) .AddSubEntry(cf7::command::sub("i", "input", "Input png|bmp", true))
.AddSubEntry(cf7::command::sub("o", "output", .AddSubEntry(cf7::command::sub("o", "output",
"Output path of .bclim file", true)) "Output path of .bclim file", true))
.AddSubEntry(cf7::command::sub("f", "format", .AddSubEntry(cf7::command::sub(
"Image format rgba32|rgb565|a8", true)) "f", "format",
"Image format "
"rgba8888|rgb888|rgba4444|rgba5551|rgb565|a8|l8|a4|l4|la4|la8",
false))
.SetFunction(BCLIMMaker)); .SetFunction(BCLIMMaker));
mgr.Execute(); mgr.Execute();
return 0; return 0;