├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── Makefile.am ├── README.md ├── autoclean ├── autogen.sh ├── configure.ac ├── m4 ├── ax_cxx_compile_stdcxx.m4 └── ax_cxx_compile_stdcxx_17.m4 └── src ├── common.cpp ├── common.hpp ├── config.h.cmake ├── decoder ├── LICENSE.txt ├── Readme.md ├── cioutil.c ├── cioutil.h ├── libsmfc.c ├── libsmfc.h ├── libsmfcx.c ├── libsmfcx.h ├── nssamp.c ├── nssamp.h ├── nsstrm.c ├── nsstrm.h ├── nsswav.c ├── nsswav.h ├── sseq2mid.c └── sseq2mid.h ├── htools └── macrotools.h ├── main.cpp ├── main.h ├── memfile.hpp ├── sdat.cpp ├── sdat.hpp ├── sdatxtract.cpp ├── sdatxtract.hpp ├── swar.c └── swar.h /.gitignore: -------------------------------------------------------------------------------- 1 | # autotools/cmake garbage. 2 | .deps 3 | .dirstamp 4 | autom4te.cache 5 | stamp-h1 6 | aclocal.m4 7 | compile 8 | config.guess 9 | config.log 10 | config.status 11 | config.sub 12 | configure 13 | depcomp 14 | install-sh 15 | missing 16 | Makefile.in 17 | Makefile 18 | config.h 19 | config.h.in 20 | CMakeFiles 21 | CMakeCache.txt 22 | cmake_install.cmake 23 | 24 | # generated files 25 | *.a 26 | *.bin 27 | *.d 28 | *.elf 29 | *.map 30 | *.o 31 | *.obj 32 | *.tsk 33 | *.sdat 34 | *.nds 35 | *.exe 36 | sdatxtract 37 | 38 | # macOS Garbage 39 | .DS_Store 40 | ._* 41 | 42 | # build directories 43 | build 44 | cmakebuild 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | notifications: 4 | email: false 5 | 6 | matrix: 7 | include: 8 | - os: linux 9 | dist: focal 10 | compiler: gcc 11 | addons: 12 | apt: 13 | packages: 14 | - gcc-9 15 | - g++-9 16 | - os: linux 17 | dist: focal 18 | compiler: clang 19 | addons: 20 | apt: 21 | packages: 22 | - clang-10 23 | - clang++-10 24 | - os: osx 25 | compiler: clang 26 | osx_image: xcode12 27 | 28 | addons: 29 | apt: 30 | sources: 31 | - ubuntu-toolchain-r-test 32 | packages: 33 | - cmake 34 | 35 | script: 36 | - mkdir build && cd build 37 | - cmake .. 38 | - make -j2 sdatxtract 39 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ########## Project setup ########## 2 | PROJECT(sdatxtract) 3 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11) 4 | 5 | # SDATxtract Version 6 | SET(VERSION_MAJOR 0) 7 | SET(VERSION_MINOR 2) 8 | SET(VERSION_MICRO 0) 9 | SET(SDATXTRACT_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}") 10 | 11 | set(CMAKE_CXX_STANDARD 17) 12 | 13 | # Find Macros 14 | SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) 15 | 16 | INCLUDE(CMakeDependentOption) 17 | INCLUDE(CheckCCompilerFlag) 18 | INCLUDE(CheckCSourceCompiles) 19 | INCLUDE(CheckIncludeFile) 20 | INCLUDE(TestBigEndian) 21 | 22 | INCLUDE_DIRECTORIES(BEFORE "${CMAKE_BINARY_DIR}/include") 23 | CONFIGURE_FILE("${CMAKE_SOURCE_DIR}/src/config.h.cmake" "${CMAKE_BINARY_DIR}/include/config.h") 24 | 25 | 26 | FILE(GLOB SOURCES "src/*.cpp" "src/*.c" "src/decoder/*.c") 27 | 28 | ADD_EXECUTABLE(sdatxtract ${SOURCES}) 29 | 30 | if(MSVC) 31 | TARGET_COMPILE_OPTIONS(sdatxtract PRIVATE /W4 /WX- -D_CRT_SECURE_NO_WARNINGS) 32 | else(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 33 | TARGET_COMPILE_OPTIONS(sdatxtract PRIVATE -Wall -Wconversion -Wsign-conversion -Wno-long-long -pedantic) 34 | endif() 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Oreo639 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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. 8 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Makefile.am -- Process this file with automake to produce Makefile.in 2 | bin_PROGRAMS = sdatxtract 3 | 4 | AM_CFLAGS = -Wall -Wno-long-long -Wconversion -pedantic 5 | 6 | sdatxtract_SOURCES = src/main.cpp src/sdatxtract.cpp src/sdat.cpp \ 7 | src/common.cpp src/decoder/cioutil.c src/decoder/libsmfc.c \ 8 | src/decoder/libsmfcx.c src/decoder/nssamp.c src/decoder/nsstrm.c \ 9 | src/decoder/nsswav.c src/decoder/sseq2mid.c src/swar.c 10 | 11 | EXTRA_DIST = autogen.sh 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SDATxtract 2 | [![Build Status](https://travis-ci.com/Oreo639/sdatxtract.svg?branch=master)](https://travis-ci.com/Oreo639/sdatxtract) 3 | 4 | A comand line Nintendo ds Sound DATa extraction utility. 5 | 6 | What is it? 7 | 8 | It is a utility for dumping sdats from nds files, extracting contained files from sdats, and converting the contained files to standard formats. 9 | 10 | You can also obtain sdats by using [godmode9](https://github.com/d0k3/GodMode9), [ndstool](https://github.com/devkitPro/ndstool), or [tinke](https://github.com/pleonex/tinke). 11 | 12 | ## Building 13 | You can use either cmake or autotools to build this program and I will cover both. 14 | ### Autotools 15 | 1. Run `./autogen.sh`. This will automaticly run all the nessary auto* tools that are needed to compile this program. 16 | 2. Run `./configure` 17 | 3. Type `make` 18 | ### Cmake 19 | 1. Run `mkdir build && cd build` 20 | 2. Then run `cmake ..` 21 | 3. Once it is done, type `make` 22 | 23 | ## Credits 24 | [Vgmtrans](https://github.com/vgmtrans/vgmtrans): Symb parser prety heavily based on it. 25 | 26 | [Loveemu](https://github.com/loveemu): Sseq2mid and strm2wav are used in this project. 27 | 28 | Ndssndext: Largely inspired by ndssndext, however do to the aplication being unlisenced, this application was rewriten. 29 | 30 | [caesar](https://github.com/kr3nshaw/caesar): Current rewrite took heavy inspiration from it. 31 | 32 | SDAT documentation; wouldn't be possible without the hard work of: 33 | + Crystal - the author of CrystalTile2.exe 34 | + loveemu - the author of sseq2mid.exe, swave2wave.exe & strm2wave.exe 35 | + Nintendon - the author of ndssndext.exe 36 | + DJ Bouche - the author of sdattool.exe 37 | + VGMTrans - the author of VGMTrans.exe 38 | + And others on documenting the format. I would never have been able to finish this without them. 39 | 40 | You can find them here: 41 | + http://www.feshrine.net/hacking/doc/nds-sdat.html 42 | + http://www.romhacking.net/documents/%5b469%5dnds_formats.htm#SDAT 43 | 44 | If I used your code without crediting you, please let me know either by relevant social media, or by the issue tracker. 45 | 46 | ## Other tools 47 | + [vgmtrans](https://github.com/vgmtrans/vgmtrans) 48 | + [VGMusicStudio](https://github.com/Kermalis/VGMusicStudio) 49 | 50 | ## Copyright 51 | Read LICENSE.md for information. 52 | -------------------------------------------------------------------------------- /autoclean: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm aclocal.m4 compile config.guess config.log config.status config.sub configure depcomp install-sh missing Makefile.in Makefile src/config.h 3 | find . -type f -name ".dirstamp" -delete 4 | find . -type f -name "stamp-h1" -delete 5 | find . -type f -name "config.h.in*" -delete 6 | find . -type d -name ".deps" -exec rm -r {} + 7 | rm -r autom4te.cache 8 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | autoheader 2 | aclocal -I m4 3 | autoconf 4 | automake -c --add-missing 5 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.61) 5 | AC_INIT(sdatxtract, 0.2.0, https://github.com/oreo639/sdatxtract/issues) 6 | AC_CONFIG_SRCDIR(src/main.cpp) 7 | AC_CONFIG_MACRO_DIR([m4]) 8 | 9 | AM_INIT_AUTOMAKE(subdir-objects foreign) 10 | 11 | AC_CONFIG_HEADERS(src/config.h) 12 | 13 | AC_DEFINE(APP_NAME, "SDATxtract", Name of application) 14 | AC_DEFINE_UNQUOTED(APP_VERSION_MAJOR, 0, Major version of application) 15 | AC_DEFINE_UNQUOTED(APP_VERSION_MINOR, 2, Minor version of application) 16 | AC_DEFINE_UNQUOTED(APP_VERSION_MICRO, 0, Micro version of application) 17 | AC_DEFINE_UNQUOTED(APP_VERSION_FULL_RAW, APP_VERSION_MAJOR.APP_VERSION_MINOR.APP_VERSION_MICRO, Full version of application) 18 | AC_DEFINE_UNQUOTED(APP_VERSION_FULL, STRINGIFY(APP_VERSION_FULL_RAW), Full version of application string) 19 | 20 | AC_CANONICAL_BUILD 21 | AC_CANONICAL_HOST 22 | 23 | AC_PROG_CC 24 | AC_PROG_CXX 25 | AC_LANG([C++], [C]) 26 | 27 | AX_CXX_COMPILE_STDCXX_17([noext],[mandatory]) 28 | 29 | AC_CONFIG_FILES(Makefile) 30 | AC_OUTPUT 31 | -------------------------------------------------------------------------------- /m4/ax_cxx_compile_stdcxx.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check for baseline language coverage in the compiler for the specified 12 | # version of the C++ standard. If necessary, add switches to CXX and 13 | # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) 14 | # or '14' (for the C++14 standard). 15 | # 16 | # The second argument, if specified, indicates whether you insist on an 17 | # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. 18 | # -std=c++11). If neither is specified, you get whatever works, with 19 | # preference for an extended mode. 20 | # 21 | # The third argument, if specified 'mandatory' or if left unspecified, 22 | # indicates that baseline support for the specified C++ standard is 23 | # required and that the macro should error out if no mode with that 24 | # support is found. If specified 'optional', then configuration proceeds 25 | # regardless, after defining HAVE_CXX${VERSION} if and only if a 26 | # supporting mode is found. 27 | # 28 | # LICENSE 29 | # 30 | # Copyright (c) 2008 Benjamin Kosnik 31 | # Copyright (c) 2012 Zack Weinberg 32 | # Copyright (c) 2013 Roy Stogner 33 | # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov 34 | # Copyright (c) 2015 Paul Norman 35 | # Copyright (c) 2015 Moritz Klammler 36 | # Copyright (c) 2016 Krzesimir Nowak 37 | # 38 | # Copying and distribution of this file, with or without modification, are 39 | # permitted in any medium without royalty provided the copyright notice 40 | # and this notice are preserved. This file is offered as-is, without any 41 | # warranty. 42 | 43 | #serial 7 44 | 45 | dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro 46 | dnl (serial version number 13). 47 | 48 | AX_REQUIRE_DEFINED([AC_MSG_WARN]) 49 | AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl 50 | m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], 51 | [$1], [14], [ax_cxx_compile_alternatives="14 1y"], 52 | [$1], [17], [ax_cxx_compile_alternatives="17 1z"], 53 | [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl 54 | m4_if([$2], [], [], 55 | [$2], [ext], [], 56 | [$2], [noext], [], 57 | [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl 58 | m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], 59 | [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], 60 | [$3], [optional], [ax_cxx_compile_cxx$1_required=false], 61 | [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) 62 | AC_LANG_PUSH([C++])dnl 63 | ac_success=no 64 | AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, 65 | ax_cv_cxx_compile_cxx$1, 66 | [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], 67 | [ax_cv_cxx_compile_cxx$1=yes], 68 | [ax_cv_cxx_compile_cxx$1=no])]) 69 | if test x$ax_cv_cxx_compile_cxx$1 = xyes; then 70 | ac_success=yes 71 | fi 72 | m4_if([$2], [noext], [], [dnl 73 | if test x$ac_success = xno; then 74 | for alternative in ${ax_cxx_compile_alternatives}; do 75 | switch="-std=gnu++${alternative}" 76 | cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) 77 | AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, 78 | $cachevar, 79 | [ac_save_CXX="$CXX" 80 | CXX="$CXX $switch" 81 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], 82 | [eval $cachevar=yes], 83 | [eval $cachevar=no]) 84 | CXX="$ac_save_CXX"]) 85 | if eval test x\$$cachevar = xyes; then 86 | CXX="$CXX $switch" 87 | if test -n "$CXXCPP" ; then 88 | CXXCPP="$CXXCPP $switch" 89 | fi 90 | ac_success=yes 91 | break 92 | fi 93 | done 94 | fi]) 95 | m4_if([$2], [ext], [], [dnl 96 | if test x$ac_success = xno; then 97 | dnl HP's aCC needs +std=c++11 according to: 98 | dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf 99 | dnl Cray's crayCC needs "-h std=c++11" 100 | for alternative in ${ax_cxx_compile_alternatives}; do 101 | for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do 102 | cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) 103 | AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, 104 | $cachevar, 105 | [ac_save_CXX="$CXX" 106 | CXX="$CXX $switch" 107 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], 108 | [eval $cachevar=yes], 109 | [eval $cachevar=no]) 110 | CXX="$ac_save_CXX"]) 111 | if eval test x\$$cachevar = xyes; then 112 | CXX="$CXX $switch" 113 | if test -n "$CXXCPP" ; then 114 | CXXCPP="$CXXCPP $switch" 115 | fi 116 | ac_success=yes 117 | break 118 | fi 119 | done 120 | if test x$ac_success = xyes; then 121 | break 122 | fi 123 | done 124 | fi]) 125 | AC_LANG_POP([C++]) 126 | if test x$ax_cxx_compile_cxx$1_required = xtrue; then 127 | if test x$ac_success = xno; then 128 | AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) 129 | fi 130 | fi 131 | if test x$ac_success = xno; then 132 | HAVE_CXX$1=0 133 | AC_MSG_NOTICE([No compiler with C++$1 support was found]) 134 | else 135 | HAVE_CXX$1=1 136 | AC_DEFINE(HAVE_CXX$1,1, 137 | [define if the compiler supports basic C++$1 syntax]) 138 | fi 139 | AC_SUBST(HAVE_CXX$1) 140 | m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) 141 | ]) 142 | 143 | 144 | dnl Test body for checking C++11 support 145 | 146 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], 147 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 148 | ) 149 | 150 | 151 | dnl Test body for checking C++14 support 152 | 153 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], 154 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 155 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 156 | ) 157 | 158 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], 159 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 160 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 161 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 162 | ) 163 | 164 | dnl Tests for new features in C++11 165 | 166 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ 167 | // If the compiler admits that it is not ready for C++11, why torture it? 168 | // Hopefully, this will speed up the test. 169 | #ifndef __cplusplus 170 | #error "This is not a C++ compiler" 171 | #elif __cplusplus < 201103L 172 | #error "This is not a C++11 compiler" 173 | #else 174 | namespace cxx11 175 | { 176 | namespace test_static_assert 177 | { 178 | template 179 | struct check 180 | { 181 | static_assert(sizeof(int) <= sizeof(T), "not big enough"); 182 | }; 183 | } 184 | namespace test_final_override 185 | { 186 | struct Base 187 | { 188 | virtual void f() {} 189 | }; 190 | struct Derived : public Base 191 | { 192 | virtual void f() override {} 193 | }; 194 | } 195 | namespace test_double_right_angle_brackets 196 | { 197 | template < typename T > 198 | struct check {}; 199 | typedef check single_type; 200 | typedef check> double_type; 201 | typedef check>> triple_type; 202 | typedef check>>> quadruple_type; 203 | } 204 | namespace test_decltype 205 | { 206 | int 207 | f() 208 | { 209 | int a = 1; 210 | decltype(a) b = 2; 211 | return a + b; 212 | } 213 | } 214 | namespace test_type_deduction 215 | { 216 | template < typename T1, typename T2 > 217 | struct is_same 218 | { 219 | static const bool value = false; 220 | }; 221 | template < typename T > 222 | struct is_same 223 | { 224 | static const bool value = true; 225 | }; 226 | template < typename T1, typename T2 > 227 | auto 228 | add(T1 a1, T2 a2) -> decltype(a1 + a2) 229 | { 230 | return a1 + a2; 231 | } 232 | int 233 | test(const int c, volatile int v) 234 | { 235 | static_assert(is_same::value == true, ""); 236 | static_assert(is_same::value == false, ""); 237 | static_assert(is_same::value == false, ""); 238 | auto ac = c; 239 | auto av = v; 240 | auto sumi = ac + av + 'x'; 241 | auto sumf = ac + av + 1.0; 242 | static_assert(is_same::value == true, ""); 243 | static_assert(is_same::value == true, ""); 244 | static_assert(is_same::value == true, ""); 245 | static_assert(is_same::value == false, ""); 246 | static_assert(is_same::value == true, ""); 247 | return (sumf > 0.0) ? sumi : add(c, v); 248 | } 249 | } 250 | namespace test_noexcept 251 | { 252 | int f() { return 0; } 253 | int g() noexcept { return 0; } 254 | static_assert(noexcept(f()) == false, ""); 255 | static_assert(noexcept(g()) == true, ""); 256 | } 257 | namespace test_constexpr 258 | { 259 | template < typename CharT > 260 | unsigned long constexpr 261 | strlen_c_r(const CharT *const s, const unsigned long acc) noexcept 262 | { 263 | return *s ? strlen_c_r(s + 1, acc + 1) : acc; 264 | } 265 | template < typename CharT > 266 | unsigned long constexpr 267 | strlen_c(const CharT *const s) noexcept 268 | { 269 | return strlen_c_r(s, 0UL); 270 | } 271 | static_assert(strlen_c("") == 0UL, ""); 272 | static_assert(strlen_c("1") == 1UL, ""); 273 | static_assert(strlen_c("example") == 7UL, ""); 274 | static_assert(strlen_c("another\0example") == 7UL, ""); 275 | } 276 | namespace test_rvalue_references 277 | { 278 | template < int N > 279 | struct answer 280 | { 281 | static constexpr int value = N; 282 | }; 283 | answer<1> f(int&) { return answer<1>(); } 284 | answer<2> f(const int&) { return answer<2>(); } 285 | answer<3> f(int&&) { return answer<3>(); } 286 | void 287 | test() 288 | { 289 | int i = 0; 290 | const int c = 0; 291 | static_assert(decltype(f(i))::value == 1, ""); 292 | static_assert(decltype(f(c))::value == 2, ""); 293 | static_assert(decltype(f(0))::value == 3, ""); 294 | } 295 | } 296 | namespace test_uniform_initialization 297 | { 298 | struct test 299 | { 300 | static const int zero {}; 301 | static const int one {1}; 302 | }; 303 | static_assert(test::zero == 0, ""); 304 | static_assert(test::one == 1, ""); 305 | } 306 | namespace test_lambdas 307 | { 308 | void 309 | test1() 310 | { 311 | auto lambda1 = [](){}; 312 | auto lambda2 = lambda1; 313 | lambda1(); 314 | lambda2(); 315 | } 316 | int 317 | test2() 318 | { 319 | auto a = [](int i, int j){ return i + j; }(1, 2); 320 | auto b = []() -> int { return '0'; }(); 321 | auto c = [=](){ return a + b; }(); 322 | auto d = [&](){ return c; }(); 323 | auto e = [a, &b](int x) mutable { 324 | const auto identity = [](int y){ return y; }; 325 | for (auto i = 0; i < a; ++i) 326 | a += b--; 327 | return x + identity(a + b); 328 | }(0); 329 | return a + b + c + d + e; 330 | } 331 | int 332 | test3() 333 | { 334 | const auto nullary = [](){ return 0; }; 335 | const auto unary = [](int x){ return x; }; 336 | using nullary_t = decltype(nullary); 337 | using unary_t = decltype(unary); 338 | const auto higher1st = [](nullary_t f){ return f(); }; 339 | const auto higher2nd = [unary](nullary_t f1){ 340 | return [unary, f1](unary_t f2){ return f2(unary(f1())); }; 341 | }; 342 | return higher1st(nullary) + higher2nd(nullary)(unary); 343 | } 344 | } 345 | namespace test_variadic_templates 346 | { 347 | template 348 | struct sum; 349 | template 350 | struct sum 351 | { 352 | static constexpr auto value = N0 + sum::value; 353 | }; 354 | template <> 355 | struct sum<> 356 | { 357 | static constexpr auto value = 0; 358 | }; 359 | static_assert(sum<>::value == 0, ""); 360 | static_assert(sum<1>::value == 1, ""); 361 | static_assert(sum<23>::value == 23, ""); 362 | static_assert(sum<1, 2>::value == 3, ""); 363 | static_assert(sum<5, 5, 11>::value == 21, ""); 364 | static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); 365 | } 366 | // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae 367 | // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function 368 | // because of this. 369 | namespace test_template_alias_sfinae 370 | { 371 | struct foo {}; 372 | template 373 | using member = typename T::member_type; 374 | template 375 | void func(...) {} 376 | template 377 | void func(member*) {} 378 | void test(); 379 | void test() { func(0); } 380 | } 381 | } // namespace cxx11 382 | #endif // __cplusplus >= 201103L 383 | ]]) 384 | 385 | 386 | dnl Tests for new features in C++14 387 | 388 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ 389 | // If the compiler admits that it is not ready for C++14, why torture it? 390 | // Hopefully, this will speed up the test. 391 | #ifndef __cplusplus 392 | #error "This is not a C++ compiler" 393 | #elif __cplusplus < 201402L 394 | #error "This is not a C++14 compiler" 395 | #else 396 | namespace cxx14 397 | { 398 | namespace test_polymorphic_lambdas 399 | { 400 | int 401 | test() 402 | { 403 | const auto lambda = [](auto&&... args){ 404 | const auto istiny = [](auto x){ 405 | return (sizeof(x) == 1UL) ? 1 : 0; 406 | }; 407 | const int aretiny[] = { istiny(args)... }; 408 | return aretiny[0]; 409 | }; 410 | return lambda(1, 1L, 1.0f, '1'); 411 | } 412 | } 413 | namespace test_binary_literals 414 | { 415 | constexpr auto ivii = 0b0000000000101010; 416 | static_assert(ivii == 42, "wrong value"); 417 | } 418 | namespace test_generalized_constexpr 419 | { 420 | template < typename CharT > 421 | constexpr unsigned long 422 | strlen_c(const CharT *const s) noexcept 423 | { 424 | auto length = 0UL; 425 | for (auto p = s; *p; ++p) 426 | ++length; 427 | return length; 428 | } 429 | static_assert(strlen_c("") == 0UL, ""); 430 | static_assert(strlen_c("x") == 1UL, ""); 431 | static_assert(strlen_c("test") == 4UL, ""); 432 | static_assert(strlen_c("another\0test") == 7UL, ""); 433 | } 434 | namespace test_lambda_init_capture 435 | { 436 | int 437 | test() 438 | { 439 | auto x = 0; 440 | const auto lambda1 = [a = x](int b){ return a + b; }; 441 | const auto lambda2 = [a = lambda1(x)](){ return a; }; 442 | return lambda2(); 443 | } 444 | } 445 | namespace test_digit_separators 446 | { 447 | constexpr auto ten_million = 100'000'000; 448 | static_assert(ten_million == 100000000, ""); 449 | } 450 | namespace test_return_type_deduction 451 | { 452 | auto f(int& x) { return x; } 453 | decltype(auto) g(int& x) { return x; } 454 | template < typename T1, typename T2 > 455 | struct is_same 456 | { 457 | static constexpr auto value = false; 458 | }; 459 | template < typename T > 460 | struct is_same 461 | { 462 | static constexpr auto value = true; 463 | }; 464 | int 465 | test() 466 | { 467 | auto x = 0; 468 | static_assert(is_same::value, ""); 469 | static_assert(is_same::value, ""); 470 | return x; 471 | } 472 | } 473 | } // namespace cxx14 474 | #endif // __cplusplus >= 201402L 475 | ]]) 476 | 477 | 478 | dnl Tests for new features in C++17 479 | 480 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ 481 | // If the compiler admits that it is not ready for C++17, why torture it? 482 | // Hopefully, this will speed up the test. 483 | #ifndef __cplusplus 484 | #error "This is not a C++ compiler" 485 | #elif __cplusplus <= 201402L 486 | #error "This is not a C++17 compiler" 487 | #else 488 | #if defined(__clang__) 489 | #define REALLY_CLANG 490 | #else 491 | #if defined(__GNUC__) 492 | #define REALLY_GCC 493 | #endif 494 | #endif 495 | #include 496 | #include 497 | #include 498 | namespace cxx17 499 | { 500 | #if !defined(REALLY_CLANG) 501 | namespace test_constexpr_lambdas 502 | { 503 | // TODO: test it with clang++ from git 504 | constexpr int foo = [](){return 42;}(); 505 | } 506 | #endif // !defined(REALLY_CLANG) 507 | namespace test::nested_namespace::definitions 508 | { 509 | } 510 | namespace test_fold_expression 511 | { 512 | template 513 | int multiply(Args... args) 514 | { 515 | return (args * ... * 1); 516 | } 517 | template 518 | bool all(Args... args) 519 | { 520 | return (args && ...); 521 | } 522 | } 523 | namespace test_extended_static_assert 524 | { 525 | static_assert (true); 526 | } 527 | namespace test_auto_brace_init_list 528 | { 529 | auto foo = {5}; 530 | auto bar {5}; 531 | static_assert(std::is_same, decltype(foo)>::value); 532 | static_assert(std::is_same::value); 533 | } 534 | namespace test_typename_in_template_template_parameter 535 | { 536 | template typename X> struct D; 537 | } 538 | namespace test_fallthrough_nodiscard_maybe_unused_attributes 539 | { 540 | int f1() 541 | { 542 | return 42; 543 | } 544 | [[nodiscard]] int f2() 545 | { 546 | [[maybe_unused]] auto unused = f1(); 547 | switch (f1()) 548 | { 549 | case 17: 550 | f1(); 551 | [[fallthrough]]; 552 | case 42: 553 | f1(); 554 | } 555 | return f1(); 556 | } 557 | } 558 | namespace test_extended_aggregate_initialization 559 | { 560 | struct base1 561 | { 562 | int b1, b2 = 42; 563 | }; 564 | struct base2 565 | { 566 | base2() { 567 | b3 = 42; 568 | } 569 | int b3; 570 | }; 571 | struct derived : base1, base2 572 | { 573 | int d; 574 | }; 575 | derived d1 {{1, 2}, {}, 4}; // full initialization 576 | derived d2 {{}, {}, 4}; // value-initialized bases 577 | } 578 | namespace test_general_range_based_for_loop 579 | { 580 | struct iter 581 | { 582 | int i; 583 | int& operator* () 584 | { 585 | return i; 586 | } 587 | const int& operator* () const 588 | { 589 | return i; 590 | } 591 | iter& operator++() 592 | { 593 | ++i; 594 | return *this; 595 | } 596 | }; 597 | struct sentinel 598 | { 599 | int i; 600 | }; 601 | bool operator== (const iter& i, const sentinel& s) 602 | { 603 | return i.i == s.i; 604 | } 605 | bool operator!= (const iter& i, const sentinel& s) 606 | { 607 | return !(i == s); 608 | } 609 | struct range 610 | { 611 | iter begin() const 612 | { 613 | return {0}; 614 | } 615 | sentinel end() const 616 | { 617 | return {5}; 618 | } 619 | }; 620 | void f() 621 | { 622 | range r {}; 623 | for (auto i : r) 624 | { 625 | [[maybe_unused]] auto v = i; 626 | } 627 | } 628 | } 629 | namespace test_lambda_capture_asterisk_this_by_value 630 | { 631 | struct t 632 | { 633 | int i; 634 | int foo() 635 | { 636 | return [*this]() 637 | { 638 | return i; 639 | }(); 640 | } 641 | }; 642 | } 643 | namespace test_enum_class_construction 644 | { 645 | enum class byte : unsigned char 646 | {}; 647 | byte foo {42}; 648 | } 649 | namespace test_constexpr_if 650 | { 651 | template 652 | int f () 653 | { 654 | if constexpr(cond) 655 | { 656 | return 13; 657 | } 658 | else 659 | { 660 | return 42; 661 | } 662 | } 663 | } 664 | namespace test_selection_statement_with_initializer 665 | { 666 | int f() 667 | { 668 | return 13; 669 | } 670 | int f2() 671 | { 672 | if (auto i = f(); i > 0) 673 | { 674 | return 3; 675 | } 676 | switch (auto i = f(); i + 4) 677 | { 678 | case 17: 679 | return 2; 680 | default: 681 | return 1; 682 | } 683 | } 684 | } 685 | #if !defined(REALLY_CLANG) 686 | namespace test_template_argument_deduction_for_class_templates 687 | { 688 | // TODO: test it with clang++ from git 689 | template 690 | struct pair 691 | { 692 | pair (T1 p1, T2 p2) 693 | : m1 {p1}, 694 | m2 {p2} 695 | {} 696 | T1 m1; 697 | T2 m2; 698 | }; 699 | void f() 700 | { 701 | [[maybe_unused]] auto p = pair{13, 42u}; 702 | } 703 | } 704 | #endif // !defined(REALLY_CLANG) 705 | namespace test_non_type_auto_template_parameters 706 | { 707 | template 708 | struct B 709 | {}; 710 | B<5> b1; 711 | B<'a'> b2; 712 | } 713 | #if !defined(REALLY_CLANG) 714 | namespace test_structured_bindings 715 | { 716 | // TODO: test it with clang++ from git 717 | int arr[2] = { 1, 2 }; 718 | std::pair pr = { 1, 2 }; 719 | auto f1() -> int(&)[2] 720 | { 721 | return arr; 722 | } 723 | auto f2() -> std::pair& 724 | { 725 | return pr; 726 | } 727 | struct S 728 | { 729 | int x1 : 2; 730 | volatile double y1; 731 | }; 732 | S f3() 733 | { 734 | return {}; 735 | } 736 | auto [ x1, y1 ] = f1(); 737 | auto& [ xr1, yr1 ] = f1(); 738 | auto [ x2, y2 ] = f2(); 739 | auto& [ xr2, yr2 ] = f2(); 740 | const auto [ x3, y3 ] = f3(); 741 | } 742 | #endif // !defined(REALLY_CLANG) 743 | #if !defined(REALLY_CLANG) 744 | namespace test_exception_spec_type_system 745 | { 746 | // TODO: test it with clang++ from git 747 | struct Good {}; 748 | struct Bad {}; 749 | void g1() noexcept; 750 | void g2(); 751 | template 752 | Bad 753 | f(T*, T*); 754 | template 755 | Good 756 | f(T1*, T2*); 757 | static_assert (std::is_same_v); 758 | } 759 | #endif // !defined(REALLY_CLANG) 760 | namespace test_inline_variables 761 | { 762 | template void f(T) 763 | {} 764 | template inline T g(T) 765 | { 766 | return T{}; 767 | } 768 | template<> inline void f<>(int) 769 | {} 770 | template<> int g<>(int) 771 | { 772 | return 5; 773 | } 774 | } 775 | } // namespace cxx17 776 | #endif // __cplusplus <= 201402L 777 | ]]) 778 | -------------------------------------------------------------------------------- /m4/ax_cxx_compile_stdcxx_17.m4: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_17.html 3 | # ============================================================================= 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CXX_COMPILE_STDCXX_17([ext|noext], [mandatory|optional]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check for baseline language coverage in the compiler for the C++17 12 | # standard; if necessary, add switches to CXX and CXXCPP to enable 13 | # support. 14 | # 15 | # This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX 16 | # macro with the version set to C++17. The two optional arguments are 17 | # forwarded literally as the second and third argument respectively. 18 | # Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for 19 | # more information. If you want to use this macro, you also need to 20 | # download the ax_cxx_compile_stdcxx.m4 file. 21 | # 22 | # LICENSE 23 | # 24 | # Copyright (c) 2015 Moritz Klammler 25 | # Copyright (c) 2016 Krzesimir Nowak 26 | # 27 | # Copying and distribution of this file, with or without modification, are 28 | # permitted in any medium without royalty provided the copyright notice 29 | # and this notice are preserved. This file is offered as-is, without any 30 | # warranty. 31 | 32 | #serial 2 33 | 34 | AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) 35 | AC_DEFUN([AX_CXX_COMPILE_STDCXX_17], [AX_CXX_COMPILE_STDCXX([17], [$1], [$2])]) 36 | -------------------------------------------------------------------------------- /src/common.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "common.hpp" 10 | 11 | bool Common::ShowWarnings = false; 12 | std::vector Common::Log; 13 | 14 | void Common::Warning(MemFile& mf, std::string msg) 15 | { 16 | if (ShowWarnings) 17 | { 18 | std::cerr << std::hex << std::setfill('0') << std::uppercase << std::endl; 19 | std::cerr << "WARNING IN\t" << mf.GetFilename() << std::endl; 20 | std::cerr << "AT POSITION\t0x" << std::setw(8) << mf.GetOffset() << std::endl; 21 | std::cerr << "MESSAGE\t\t" << msg << std::endl; 22 | std::cerr << std::endl; 23 | } 24 | } 25 | 26 | void Common::Analyse(MemFile& mf, std::string tag, uint32_t val) 27 | { 28 | Common::Log.push_back(mf.GetFilename() + "," + tag + "," + std::to_string(val)); 29 | } 30 | 31 | void Common::Dump(std::string fileName) 32 | { 33 | std::ofstream ofs(fileName); 34 | ofs << "fileName,tag,val" << std::endl; 35 | 36 | for (size_t i = 0; i < Common::Log.size(); ++i) 37 | { 38 | ofs << Common::Log[i] << std::endl; 39 | } 40 | 41 | ofs.close(); 42 | } 43 | -------------------------------------------------------------------------------- /src/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "memfile.hpp" 12 | 13 | struct Common 14 | { 15 | static bool ShowWarnings; 16 | static std::stack FileNames; 17 | static std::vector Log; 18 | 19 | template 20 | static bool Assert(MemFile& mf, T expected, T found) 21 | { 22 | if (found != expected) 23 | { 24 | std::cerr << std::hex << std::setfill('0') << std::uppercase << std::endl; 25 | std::cerr << "ERROR IN\t" << mf.GetFilename() << std::endl; 26 | std::cerr << "AT POSITION\t0x" << std::setw(8) << mf.GetOffset() << std::endl; 27 | std::cerr << "BYTES READ\t0x" << std::setw(8) << sizeof(T) << std::endl; 28 | std::cerr << "EXPECTED\t0x" << std::setw(8) << expected << std::endl; 29 | std::cerr << "INSTEAD GOT\t0x" << std::setw(8) << found << std::endl; 30 | std::cerr << std::endl; 31 | 32 | return false; 33 | } 34 | 35 | return true; 36 | } 37 | 38 | template 39 | static void Error(MemFile& mf, std::string expected, T found) 40 | { 41 | std::cerr << std::hex << std::setfill('0') << std::uppercase << std::endl; 42 | std::cerr << "ERROR IN\t" << mf.GetFilename() << std::endl; 43 | std::cerr << "AT POSITION\t0x" << std::setw(8) << mf.GetOffset() << std::endl; 44 | std::cerr << "BYTES READ\t0x" << std::setw(8) << sizeof(T) << std::endl; 45 | std::cerr << "EXPECTED\t" << expected << std::endl; 46 | std::cerr << "INSTEAD GOT\t0x" << std::setw(8) << found << std::endl; 47 | std::cerr << std::endl; 48 | } 49 | 50 | static void Warning(MemFile& mf, std::string msg); 51 | static void Analyse(MemFile& mf, std::string tag, uint32_t val); 52 | static void Dump(std::string fileName); 53 | }; 54 | -------------------------------------------------------------------------------- /src/config.h.cmake: -------------------------------------------------------------------------------- 1 | /* config.h -- generated from config.h.cmake */ 2 | /* Define to the full name of this package. */ 3 | #define APP_NAME "SDATxtract" 4 | 5 | /* Define to the version of this package. */ 6 | #define APP_VERSION_FULL "@SDATXTRACT_VERSION@" 7 | /* Define to the major version of this package. */ 8 | #define APP_VERSION_MAJOR "@VERSION_MAJOR@" 9 | /* Define to the minor version of this package. */ 10 | #define APP_VERSION_MINOR "@VERSION_MINOR@" 11 | /* Define to the micro version of this package. */ 12 | #define APP_VERSION_MICRO "@VERSION_MICRO@" 13 | -------------------------------------------------------------------------------- /src/decoder/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 loveemu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/decoder/Readme.md: -------------------------------------------------------------------------------- 1 | # Decoders 2 | These are all made by loveemu, which you can find here: https://github.com/loveemu/loveemu-lab/tree/master/nds 3 | 4 | Loveemu's lisences apply for these files. 5 | -------------------------------------------------------------------------------- /src/decoder/cioutil.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ciotuil.c: simple i/o routines for C 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include "cioutil.h" 9 | 10 | 11 | /* remove path extention (SUPPORTS ASCII ONLY!) */ 12 | char* removeExt(char* path) 13 | { 14 | size_t i; 15 | 16 | i = strlen(path); 17 | if(i > 1) 18 | { 19 | i--; 20 | for(; i > 0; i--) 21 | { 22 | char c = path[i]; 23 | 24 | if(c == '.') 25 | { 26 | path[i] = '\0'; 27 | break; 28 | } 29 | else if(c == '/' || c == '\\') 30 | { 31 | break; 32 | } 33 | } 34 | } 35 | return path; 36 | } 37 | 38 | /* unsigned byte to signed */ 39 | int utos1(unsigned int value) 40 | { 41 | return (value & 0x80) ? -(signed)(value ^ 0xff)-1 : value; 42 | } 43 | 44 | /* unsigned 2bytes to signed */ 45 | int utos2(unsigned int value) 46 | { 47 | return (value & 0x8000) ? -(signed)(value ^ 0xffff)-1 : value; 48 | } 49 | 50 | /* unsigned 3bytes to signed */ 51 | int utos3(unsigned int value) 52 | { 53 | return (value & 0x800000) ? -(signed)(value ^ 0xffffff)-1 : value; 54 | } 55 | 56 | /* unsigned 4bytes to signed */ 57 | int utos4(unsigned int value) 58 | { 59 | return (value & 0x80000000) ? -(signed)(value ^ 0xffffffff)-1 : value; 60 | } 61 | 62 | /* get a byte */ 63 | int mget1(const byte* data) 64 | { 65 | return data[0]; 66 | } 67 | 68 | /* get 2bytes as little-endian */ 69 | int mget2l(const byte* data) 70 | { 71 | return data[0] | (data[1] * 0x0100); 72 | } 73 | 74 | /* get 3bytes as little-endian */ 75 | int mget3l(const byte* data) 76 | { 77 | return data[0] | (data[1] * 0x0100) | (data[2] * 0x010000); 78 | } 79 | 80 | /* get 4bytes as little-endian */ 81 | int mget4l(const byte* data) 82 | { 83 | return data[0] | (data[1] * 0x0100) | (data[2] * 0x010000) | (data[3] * 0x01000000); 84 | } 85 | 86 | /* get 2bytes as big-endian */ 87 | int mget2b(const byte* data) 88 | { 89 | return data[1] | (data[0] * 0x0100); 90 | } 91 | 92 | /* get 3bytes as big-endian */ 93 | int mget3b(const byte* data) 94 | { 95 | return data[2] | (data[1] * 0x0100) | (data[0] * 0x010000); 96 | } 97 | 98 | /* get 4bytes as big-endian */ 99 | int mget4b(const byte* data) 100 | { 101 | return data[3] | (data[2] * 0x0100) | (data[1] * 0x010000) | (data[0] * 0x01000000); 102 | } 103 | 104 | /* put a byte */ 105 | int mput1(int value, byte* data) 106 | { 107 | int lastPut = value; 108 | data[0] = lastPut & 0xff; 109 | return lastPut & 0xff; 110 | } 111 | 112 | /* put 2bytes as little-endian */ 113 | int mput2l(int value, byte* data) 114 | { 115 | int lastPut = value; 116 | data[0] = lastPut & 0xff; 117 | lastPut /= 0x0100; 118 | data[1] = lastPut & 0xff; 119 | return lastPut & 0xff; 120 | } 121 | 122 | /* put 3bytes as little-endian */ 123 | int mput3l(int value, byte* data) 124 | { 125 | int lastPut = value; 126 | data[0] = lastPut & 0xff; 127 | lastPut /= 0x0100; 128 | data[1] = lastPut & 0xff; 129 | lastPut /= 0x0100; 130 | data[2] = lastPut & 0xff; 131 | return lastPut & 0xff; 132 | } 133 | 134 | /* put 4bytes as little-endian */ 135 | int mput4l(int value, byte* data) 136 | { 137 | int lastPut = value; 138 | data[0] = lastPut & 0xff; 139 | lastPut /= 0x0100; 140 | data[1] = lastPut & 0xff; 141 | lastPut /= 0x0100; 142 | data[2] = lastPut & 0xff; 143 | lastPut /= 0x0100; 144 | data[3] = lastPut & 0xff; 145 | return lastPut & 0xff; 146 | } 147 | 148 | /* put 2bytes as big-endian */ 149 | int mput2b(int value, byte* data) 150 | { 151 | int lastPut = value; 152 | data[1] = lastPut & 0xff; 153 | lastPut /= 0x0100; 154 | data[0] = lastPut & 0xff; 155 | return lastPut & 0xff; 156 | } 157 | 158 | /* put 3bytes as big-endian */ 159 | int mput3b(int value, byte* data) 160 | { 161 | int lastPut = value; 162 | data[2] = lastPut & 0xff; 163 | lastPut /= 0x0100; 164 | data[1] = lastPut & 0xff; 165 | lastPut /= 0x0100; 166 | data[0] = lastPut & 0xff; 167 | return lastPut & 0xff; 168 | } 169 | 170 | /* put 4bytes as big-endian */ 171 | int mput4b(int value, byte* data) 172 | { 173 | int lastPut = value; 174 | data[3] = lastPut & 0xff; 175 | lastPut /= 0x0100; 176 | data[2] = lastPut & 0xff; 177 | lastPut /= 0x0100; 178 | data[1] = lastPut & 0xff; 179 | lastPut /= 0x0100; 180 | data[0] = lastPut & 0xff; 181 | return lastPut & 0xff; 182 | } 183 | 184 | /* get a byte from file */ 185 | int fget1(FILE* stream) 186 | { 187 | return fgetc(stream); 188 | } 189 | 190 | /* get 2bytes as little-endian from file */ 191 | int fget2l(FILE* stream) 192 | { 193 | int b1; 194 | int b2; 195 | 196 | b1 = fgetc(stream); 197 | b2 = fgetc(stream); 198 | if((b1 != EOF) && (b2 != EOF)) 199 | { 200 | return b1 | (b2 * 0x0100); 201 | } 202 | return EOF; 203 | } 204 | 205 | /* get 3bytes as little-endian from file */ 206 | int fget3l(FILE* stream) 207 | { 208 | int b1; 209 | int b2; 210 | int b3; 211 | 212 | b1 = fgetc(stream); 213 | b2 = fgetc(stream); 214 | b3 = fgetc(stream); 215 | if((b1 != EOF) && (b2 != EOF) && (b3 != EOF)) 216 | { 217 | return b1 | (b2 * 0x0100) | (b3 * 0x010000); 218 | } 219 | return EOF; 220 | } 221 | 222 | /* get 4bytes as little-endian from file */ 223 | int fget4l(FILE* stream) 224 | { 225 | int b1; 226 | int b2; 227 | int b3; 228 | int b4; 229 | 230 | b1 = fgetc(stream); 231 | b2 = fgetc(stream); 232 | b3 = fgetc(stream); 233 | b4 = fgetc(stream); 234 | if((b1 != EOF) && (b2 != EOF) && (b3 != EOF) && (b4 != EOF)) 235 | { 236 | return b1 | (b2 * 0x0100) | (b3 * 0x010000) | (b4 * 0x01000000); 237 | } 238 | return EOF; 239 | } 240 | 241 | /* get 2bytes as big-endian from file */ 242 | int fget2b(FILE* stream) 243 | { 244 | int b1; 245 | int b2; 246 | 247 | b1 = fgetc(stream); 248 | b2 = fgetc(stream); 249 | if((b1 != EOF) && (b2 != EOF)) 250 | { 251 | return b2 | (b1 * 0x0100); 252 | } 253 | return EOF; 254 | } 255 | 256 | /* get 3bytes as big-endian from file */ 257 | int fget3b(FILE* stream) 258 | { 259 | int b1; 260 | int b2; 261 | int b3; 262 | 263 | b1 = fgetc(stream); 264 | b2 = fgetc(stream); 265 | b3 = fgetc(stream); 266 | if((b1 != EOF) && (b2 != EOF) && (b3 != EOF)) 267 | { 268 | return b3 | (b2 * 0x0100) | (b1 * 0x010000); 269 | } 270 | return EOF; 271 | } 272 | 273 | /* get 4bytes as big-endian from file */ 274 | int fget4b(FILE* stream) 275 | { 276 | int b1; 277 | int b2; 278 | int b3; 279 | int b4; 280 | 281 | b1 = fgetc(stream); 282 | b2 = fgetc(stream); 283 | b3 = fgetc(stream); 284 | b4 = fgetc(stream); 285 | if((b1 != EOF) && (b2 != EOF) && (b3 != EOF) && (b4 != EOF)) 286 | { 287 | return b4 | (b3 * 0x0100) | (b2 * 0x010000) | (b1 * 0x01000000); 288 | } 289 | return EOF; 290 | } 291 | 292 | /* put a byte to file */ 293 | int fput1(int value, FILE* stream) 294 | { 295 | return fputc(value & 0xff, stream); 296 | } 297 | 298 | /* put 2bytes as little-endian to file */ 299 | int fput2l(int value, FILE* stream) 300 | { 301 | int result; 302 | 303 | result = fputc(value & 0xff, stream); 304 | if(result != EOF) 305 | { 306 | result = fputc((value >> 8) & 0xff, stream); 307 | } 308 | return result; 309 | } 310 | 311 | /* put 3bytes as little-endian to file */ 312 | int fput3l(int value, FILE* stream) 313 | { 314 | int result; 315 | 316 | result = fputc(value & 0xff, stream); 317 | if(result != EOF) 318 | { 319 | result = fputc((value >> 8) & 0xff, stream); 320 | if(result != EOF) 321 | { 322 | result = fputc((value >> 16) & 0xff, stream); 323 | } 324 | } 325 | return result; 326 | } 327 | 328 | /* put 4bytes as little-endian to file */ 329 | int fput4l(int value, FILE* stream) 330 | { 331 | int result; 332 | 333 | result = fputc(value & 0xff, stream); 334 | if(result != EOF) 335 | { 336 | result = fputc((value >> 8) & 0xff, stream); 337 | if(result != EOF) 338 | { 339 | result = fputc((value >> 16) & 0xff, stream); 340 | if(result != EOF) 341 | { 342 | result = fputc((value >> 24) & 0xff, stream); 343 | } 344 | } 345 | } 346 | return result; 347 | } 348 | 349 | /* put 2bytes as big-endian to file */ 350 | int fput2b(int value, FILE* stream) 351 | { 352 | int result; 353 | 354 | result = fputc((value >> 8) & 0xff, stream); 355 | if(result != EOF) 356 | { 357 | result = fputc(value & 0xff, stream); 358 | } 359 | return result; 360 | } 361 | 362 | /* put 3bytes as big-endian to file */ 363 | int fput3b(int value, FILE* stream) 364 | { 365 | int result; 366 | 367 | result = fputc((value >> 16) & 0xff, stream); 368 | if(result != EOF) 369 | { 370 | result = fputc((value >> 8) & 0xff, stream); 371 | if(result != EOF) 372 | { 373 | result = fputc(value & 0xff, stream); 374 | } 375 | } 376 | return result; 377 | } 378 | 379 | /* put 4bytes as big-endian to file */ 380 | int fput4b(int value, FILE* stream) 381 | { 382 | int result; 383 | 384 | result = fputc((value >> 24) & 0xff, stream); 385 | if(result != EOF) 386 | { 387 | result = fputc((value >> 16) & 0xff, stream); 388 | if(result != EOF) 389 | { 390 | result = fputc((value >> 8) & 0xff, stream); 391 | if(result != EOF) 392 | { 393 | result = fputc(value & 0xff, stream); 394 | } 395 | } 396 | } 397 | return result; 398 | } 399 | -------------------------------------------------------------------------------- /src/decoder/cioutil.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ciotuil.h: simple i/o routines for C 3 | */ 4 | 5 | 6 | #ifndef CIOUTIL_H 7 | #define CIOUTIL_H 8 | 9 | 10 | #include 11 | 12 | 13 | #if !defined(bool) && !defined(__cplusplus) 14 | typedef int bool; 15 | #define true 1 16 | #define false 0 17 | #endif /* !bool */ 18 | 19 | #ifndef byte 20 | typedef unsigned char byte; 21 | #endif /* !byte */ 22 | #ifndef sbyte 23 | typedef signed char sbyte; 24 | #endif /* !sbyte */ 25 | 26 | #ifndef countof 27 | #define countof(a) (sizeof(a) / sizeof(a[0])) 28 | #endif 29 | 30 | char* removeExt(char* path); 31 | 32 | /* unsigned byte to signed */ 33 | int utos1(unsigned int value); 34 | 35 | /* unsigned 2bytes to signed */ 36 | int utos2(unsigned int value); 37 | 38 | /* unsigned 3bytes to signed */ 39 | int utos3(unsigned int value); 40 | 41 | /* unsigned 4bytes to signed */ 42 | int utos4(unsigned int value); 43 | 44 | /* get a byte */ 45 | int mget1(const byte* data); 46 | 47 | /* get 2bytes as little-endian */ 48 | int mget2l(const byte* data); 49 | 50 | /* get 3bytes as little-endian */ 51 | int mget3l(const byte* data); 52 | 53 | /* get 4bytes as little-endian */ 54 | int mget4l(const byte* data); 55 | 56 | /* get 2bytes as big-endian */ 57 | int mget2b(const byte* data); 58 | 59 | /* get 3bytes as big-endian */ 60 | int mget3b(const byte* data); 61 | 62 | /* get 4bytes as big-endian */ 63 | int mget4b(const byte* data); 64 | 65 | /* put a byte */ 66 | int mput1(int value, byte* data); 67 | 68 | /* put 2bytes as little-endian */ 69 | int mput2l(int value, byte* data); 70 | 71 | /* put 3bytes as little-endian */ 72 | int mput3l(int value, byte* data); 73 | 74 | /* put 4bytes as little-endian */ 75 | int mput4l(int value, byte* data); 76 | 77 | /* put 2bytes as big-endian */ 78 | int mput2b(int value, byte* data); 79 | 80 | /* put 3bytes as big-endian */ 81 | int mput3b(int value, byte* data); 82 | 83 | /* put 4bytes as big-endian */ 84 | int mput4b(int value, byte* data); 85 | 86 | /* get a byte from file */ 87 | int fget1(FILE* stream); 88 | 89 | /* get 2bytes as little-endian from file */ 90 | int fget2l(FILE* stream); 91 | 92 | /* get 3bytes as little-endian from file */ 93 | int fget3l(FILE* stream); 94 | 95 | /* get 4bytes as little-endian from file */ 96 | int fget4l(FILE* stream); 97 | 98 | /* get 2bytes as big-endian from file */ 99 | int fget2b(FILE* stream); 100 | 101 | /* get 3bytes as big-endian from file */ 102 | int fget3b(FILE* stream); 103 | 104 | /* get 4bytes as big-endian from file */ 105 | int fget4b(FILE* stream); 106 | 107 | /* put a byte to file */ 108 | int fput1(int value, FILE* stream); 109 | 110 | /* put 2bytes as little-endian to file */ 111 | int fput2l(int value, FILE* stream); 112 | 113 | /* put 3bytes as little-endian to file */ 114 | int fput3l(int value, FILE* stream); 115 | 116 | /* put 4bytes as little-endian to file */ 117 | int fput4l(int value, FILE* stream); 118 | 119 | /* put 2bytes as big-endian to file */ 120 | int fput2b(int value, FILE* stream); 121 | 122 | /* put 3bytes as big-endian to file */ 123 | int fput3b(int value, FILE* stream); 124 | 125 | /* put 4bytes as big-endian to file */ 126 | int fput4b(int value, FILE* stream); 127 | 128 | 129 | #endif /* !CIOUTIL_H */ 130 | -------------------------------------------------------------------------------- /src/decoder/libsmfc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * libsmfc.c: simple standard midi writer by loveemu 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "libsmfc.h" 11 | 12 | #define SMF_VARLEN_MAX 4 13 | #define SMF_TIMEBASE_MAX 0x7fff 14 | #define SMF_CHANNEL_MAX 0x0f 15 | #define SMF_PORT_MAX 0xff 16 | #define SMF_VCHANNEL_MAX (SMF_CHANNEL_MAX * SMF_PORT_MAX) 17 | 18 | #define SMF_EVENT_MASK_CHANNEL 0x0f 19 | #define SMF_EVENT_MASK_MESSAGE 0xf0 20 | 21 | #define SMF_EVENT_NOTEOFF 0x80 22 | #define SMF_EVENT_NOTEON 0x90 23 | #define SMF_EVENT_KEYPRESS 0xa0 24 | #define SMF_EVENT_CONTROL 0xb0 25 | #define SMF_EVENT_PROGRAM 0xc0 26 | #define SMF_EVENT_CHANPRESS 0xd0 27 | #define SMF_EVENT_PITCHBEND 0xe0 28 | #define SMF_EVENT_SYSEX 0xf0 29 | #define SMF_EVENT_SYSEXLITE 0xf7 30 | #define SMF_EVENT_META 0xff 31 | 32 | #define SMF_MTHD_SIZE 14 33 | #define SMF_MTRK_SIZE 8 34 | 35 | unsigned int smfReadVarLength(byte* buffer, size_t bufferSize) 36 | { 37 | unsigned int value; 38 | size_t transferedSize = 0; 39 | size_t maxSizeToTransfer = (SMF_VARLEN_MAX < bufferSize) 40 | ? SMF_VARLEN_MAX : bufferSize; 41 | 42 | value = buffer[transferedSize] & 0x7f; 43 | while((transferedSize < maxSizeToTransfer) && (buffer[transferedSize] & 0x80)) 44 | { 45 | transferedSize++; 46 | value = value << 7; 47 | value |= buffer[transferedSize] & 0x7f; 48 | } 49 | return value; 50 | } 51 | 52 | size_t smfWriteByte(size_t sizeToTransfer, unsigned int value, byte* buffer, size_t bufferSize) 53 | { 54 | size_t transferedSize = 0; 55 | size_t realSizeToTransfer = (sizeToTransfer < bufferSize) 56 | ? sizeToTransfer : bufferSize; 57 | 58 | if(buffer && realSizeToTransfer) 59 | { 60 | size_t byteCount = sizeToTransfer - 1; 61 | 62 | for(transferedSize = 0; transferedSize < realSizeToTransfer; transferedSize++) 63 | { 64 | size_t shiftCount = byteCount * 8; 65 | 66 | buffer[transferedSize] = (byte) ((value >> shiftCount) & 0xff); 67 | byteCount--; 68 | } 69 | } 70 | return transferedSize; 71 | } 72 | 73 | size_t smfGetVarLengthSize(unsigned int value) 74 | { 75 | size_t varLengthSize = 1; 76 | unsigned int leftValue = value; 77 | 78 | while((leftValue > 0x7f) && (varLengthSize < SMF_VARLEN_MAX)) 79 | { 80 | varLengthSize++; 81 | leftValue = leftValue >> 7; 82 | } 83 | return varLengthSize; 84 | } 85 | 86 | size_t smfWriteVarLength(unsigned int value, byte* buffer, size_t bufferSize) 87 | { 88 | size_t transferedSize = 0; 89 | size_t varLengthSize = smfGetVarLengthSize(value); 90 | size_t sizeToTransfer = (varLengthSize < bufferSize) 91 | ? varLengthSize : bufferSize; 92 | 93 | if(buffer && sizeToTransfer) 94 | { 95 | size_t shiftCount = (varLengthSize - 1) * 7; 96 | 97 | for(transferedSize = 0; transferedSize < (sizeToTransfer - 1); transferedSize++) 98 | { 99 | buffer[transferedSize] = (byte) ((value >> shiftCount) & 0x7f) | 0x80; 100 | shiftCount -= 7; 101 | } 102 | buffer[transferedSize] = (byte) ((value >> shiftCount) & 0x7f); 103 | transferedSize++; 104 | } 105 | return transferedSize; 106 | } 107 | 108 | 109 | bool smfEventIsNoteOff(SmfEvent* event); 110 | 111 | SmfEvent* smfEventCreate(int time, int port, const byte* data, size_t dataSize) 112 | { 113 | SmfEvent* newEvent = NULL; 114 | 115 | if(data && dataSize && (time >= 0) && (port >= 0) 116 | && (port < SMF_PORT_MAX)) 117 | { 118 | newEvent = (SmfEvent*) calloc(1, sizeof(SmfEvent)); 119 | if(newEvent) 120 | { 121 | newEvent->data = (byte*) malloc(dataSize); 122 | if(newEvent->data) 123 | { 124 | memcpy(newEvent->data, data, dataSize); 125 | newEvent->size = dataSize; 126 | newEvent->time = time; 127 | newEvent->port = port; 128 | } 129 | else 130 | { 131 | free(newEvent); 132 | newEvent = NULL; 133 | } 134 | } 135 | } 136 | return newEvent; 137 | } 138 | 139 | void smfEventDelete(SmfEvent* event) 140 | { 141 | if(event) 142 | { 143 | free(event->data); 144 | free(event); 145 | } 146 | } 147 | 148 | SmfEvent* smfEventCopy(SmfEvent* event) 149 | { 150 | SmfEvent* newEvent = NULL; 151 | 152 | if(event) 153 | { 154 | newEvent = smfEventCreate(event->time, event->port, event->data, event->size); 155 | } 156 | return newEvent; 157 | } 158 | 159 | size_t smfEventGetSize(SmfEvent* event) 160 | { 161 | return event ? event->size : 0; 162 | } 163 | 164 | size_t smfEventWrite(SmfEvent* event, byte* buffer, size_t bufferSize) 165 | { 166 | size_t transferedSize = 0; 167 | 168 | if(event && buffer && bufferSize) 169 | { 170 | size_t sizeToTransfer = (event->size < bufferSize) 171 | ? event->size : bufferSize; 172 | 173 | memcpy(buffer, event->data, sizeToTransfer); 174 | transferedSize += sizeToTransfer; 175 | } 176 | 177 | return transferedSize; 178 | } 179 | 180 | int smfEventCompare(SmfEvent* event, SmfEvent* targetEvent) 181 | { 182 | int result = 0; 183 | 184 | if(event && targetEvent) 185 | { 186 | result = event->time - targetEvent->time; 187 | if(result == 0) 188 | { 189 | bool eventIsNoteOff = smfEventIsNoteOff(event); 190 | bool targetEventIsNoteOff = smfEventIsNoteOff(targetEvent); 191 | 192 | if(!eventIsNoteOff && targetEventIsNoteOff) 193 | { 194 | result = 1; 195 | } 196 | else if(eventIsNoteOff && !targetEventIsNoteOff) 197 | { 198 | result = -1; 199 | } 200 | else 201 | { 202 | result = 0; 203 | } 204 | } 205 | } 206 | return result; 207 | } 208 | 209 | bool smfEventIsNoteOff(SmfEvent* event) 210 | { 211 | bool eventIsNoteOff = false; 212 | 213 | if(event) 214 | { 215 | byte eventMessage = (event->data[0] & SMF_EVENT_MASK_MESSAGE); 216 | 217 | switch(eventMessage) 218 | { 219 | case SMF_EVENT_NOTEOFF: 220 | eventIsNoteOff = true; 221 | break; 222 | 223 | case SMF_EVENT_NOTEON: 224 | if(3 <= event->size) 225 | { 226 | int velocity = event->data[2]; 227 | 228 | eventIsNoteOff = (velocity == 0); 229 | } 230 | break; 231 | 232 | default: 233 | break; 234 | } 235 | } 236 | return eventIsNoteOff; 237 | } 238 | 239 | 240 | typedef bool (SmfTrackEnumEventsProc)(SmfEvent*, void*); 241 | bool smfTrackEnumEvents(SmfTrack* track, SmfTrackEnumEventsProc* eventProc, void* customData); 242 | 243 | typedef struct TagSmfTrackGetSizeProcInfo 244 | { 245 | int prevEventTime; 246 | size_t trackSize; 247 | } SmfTrackGetSizeProcInfo; 248 | bool smfTrackGetSizeProc(SmfEvent* event, void* customData); 249 | 250 | typedef struct TagSmfTrackWriteProcInfo 251 | { 252 | int prevEventTime; 253 | byte* buffer; 254 | size_t bufferSize; 255 | size_t transferedSize; 256 | } SmfTrackWriteProcInfo; 257 | bool smfTrackWriteProc(SmfEvent* event, void* customData); 258 | 259 | SmfTrack* smfTrackCreate(void) 260 | { 261 | SmfTrack* newTrack; 262 | 263 | newTrack = (SmfTrack*) calloc(1, sizeof(SmfTrack)); 264 | if(newTrack) 265 | { 266 | const byte endOfTrackData[] = { 0xff, 0x2f, 0x00 }; 267 | SmfEvent* endOfTrack; 268 | 269 | endOfTrack = smfEventCreate(0, 0, endOfTrackData, sizeof(endOfTrackData)); 270 | if(endOfTrack) 271 | { 272 | newTrack->firstEvent = endOfTrack; 273 | newTrack->lastEvent = endOfTrack; 274 | } 275 | else 276 | { 277 | free(newTrack); 278 | newTrack = NULL; 279 | } 280 | } 281 | return newTrack; 282 | } 283 | 284 | void smfTrackDelete(SmfTrack* track) 285 | { 286 | if(track) 287 | { 288 | SmfEvent* event = track->firstEvent; 289 | 290 | while(event) 291 | { 292 | SmfEvent* nextEvent = event->nextEvent; 293 | smfEventDelete(event); 294 | event = nextEvent; 295 | } 296 | } 297 | } 298 | 299 | SmfTrack* smfTrackCopy(SmfTrack* track) 300 | { 301 | SmfTrack* newTrack = NULL; 302 | 303 | if(track) 304 | { 305 | newTrack = smfTrackCreate(); 306 | if(newTrack) 307 | { 308 | SmfEvent* event = track->firstEvent; 309 | 310 | while(event != track->lastEvent) 311 | { 312 | smfTrackInsertEvent(newTrack, event->time, event->port, event->data, event->size); 313 | event = event->nextEvent; 314 | } 315 | smfTrackSetEndTiming(newTrack, smfTrackGetEndTiming(track)); 316 | } 317 | } 318 | return newTrack; 319 | } 320 | 321 | bool smfTrackInsertEvent(SmfTrack* track, int time, int port, const byte* data, size_t dataSize) 322 | { 323 | SmfEvent* newEvent = smfEventCreate(time, port, data, dataSize); 324 | 325 | if(newEvent) 326 | { 327 | SmfEvent* nextEvent; 328 | SmfEvent* prevEvent; 329 | 330 | if(newEvent->time > smfTrackGetEndTiming(track)) 331 | { 332 | smfTrackSetEndTiming(track, newEvent->time); 333 | } 334 | 335 | prevEvent = track->lastEvent->prevEvent; 336 | while(prevEvent && (smfEventCompare(newEvent, prevEvent) < 0)) 337 | { 338 | prevEvent = prevEvent->prevEvent; 339 | } 340 | nextEvent = prevEvent ? prevEvent->nextEvent : track->firstEvent; 341 | 342 | newEvent->prevEvent = prevEvent; 343 | newEvent->nextEvent = nextEvent; 344 | if(nextEvent) 345 | { 346 | nextEvent->prevEvent = newEvent; 347 | } 348 | else 349 | { 350 | track->lastEvent = newEvent; 351 | } 352 | if(prevEvent) 353 | { 354 | prevEvent->nextEvent = newEvent; 355 | } 356 | else 357 | { 358 | track->firstEvent = newEvent; 359 | } 360 | } 361 | return (bool) (newEvent != NULL); 362 | } 363 | 364 | size_t smfTrackGetSize(SmfTrack* track) 365 | { 366 | size_t trackSize = 0; 367 | 368 | if(track) 369 | { 370 | SmfTrackGetSizeProcInfo info; 371 | 372 | info.prevEventTime = 0; 373 | info.trackSize = SMF_MTRK_SIZE; 374 | smfTrackEnumEvents(track, smfTrackGetSizeProc, &info); 375 | trackSize = info.trackSize; 376 | } 377 | return trackSize; 378 | } 379 | 380 | bool smfTrackGetSizeProc(SmfEvent* event, void* customData) 381 | { 382 | SmfTrackGetSizeProcInfo* info = (SmfTrackGetSizeProcInfo*) customData; 383 | int deltaTime = event->time - info->prevEventTime; 384 | size_t deltaTimeSize = smfGetVarLengthSize(deltaTime); 385 | 386 | info->trackSize += deltaTimeSize; 387 | info->trackSize += event->size; 388 | info->prevEventTime = event->time; 389 | return true; 390 | } 391 | 392 | size_t smfTrackWrite(SmfTrack* track, byte* buffer, size_t bufferSize) 393 | { 394 | size_t transferedSize = 0; 395 | 396 | if(track && buffer && bufferSize) 397 | { 398 | byte MTrkData[SMF_MTRK_SIZE] = { 'M', 'T', 'r', 'k', 0, 0, 0, 0 }; 399 | size_t trackSize = smfTrackGetSize(track); 400 | 401 | smfWriteByte(4, (unsigned int) (trackSize - SMF_MTRK_SIZE), &MTrkData[4], 4); 402 | if(bufferSize >= SMF_MTRK_SIZE) 403 | { 404 | SmfTrackWriteProcInfo info; 405 | 406 | memcpy(&buffer[transferedSize], MTrkData, SMF_MTRK_SIZE); 407 | transferedSize += SMF_MTRK_SIZE; 408 | 409 | info.prevEventTime = 0; 410 | info.buffer = buffer; 411 | info.bufferSize = bufferSize; 412 | info.transferedSize = transferedSize; 413 | smfTrackEnumEvents(track, smfTrackWriteProc, &info); 414 | transferedSize = info.transferedSize; 415 | } 416 | else 417 | { 418 | memcpy(&buffer[transferedSize], MTrkData, bufferSize - transferedSize); 419 | transferedSize = bufferSize; 420 | } 421 | } 422 | return transferedSize; 423 | } 424 | 425 | bool smfTrackWriteProc(SmfEvent* event, void* customData) 426 | { 427 | bool result = false; 428 | SmfTrackWriteProcInfo* info = (SmfTrackWriteProcInfo*) customData; 429 | byte* buffer = info->buffer; 430 | size_t bufferSize = info->bufferSize; 431 | size_t transferedSize = info->transferedSize; 432 | int deltaTime = event->time - info->prevEventTime; 433 | size_t deltaTimeSize = smfGetVarLengthSize(deltaTime); 434 | 435 | if(bufferSize >= (transferedSize + deltaTimeSize)) 436 | { 437 | smfWriteVarLength(deltaTime, &buffer[transferedSize], deltaTimeSize); 438 | transferedSize += deltaTimeSize; 439 | 440 | if(bufferSize >= (transferedSize + event->size)) 441 | { 442 | memcpy(&buffer[transferedSize], event->data, event->size); 443 | transferedSize += event->size; 444 | result = true; 445 | } 446 | else 447 | { 448 | memcpy(&buffer[transferedSize], event->data, bufferSize - transferedSize); 449 | transferedSize = bufferSize; 450 | } 451 | } 452 | else 453 | { 454 | smfWriteVarLength(deltaTime, &buffer[transferedSize], bufferSize - transferedSize); 455 | transferedSize = bufferSize; 456 | } 457 | 458 | info->prevEventTime = event->time; 459 | info->transferedSize = transferedSize; 460 | return result; 461 | } 462 | 463 | bool smfTrackEnumEvents(SmfTrack* track, SmfTrackEnumEventsProc* eventProc, void* customData) 464 | { 465 | bool result = false; 466 | 467 | if(track && eventProc) 468 | { 469 | int prevEventPort = 0; 470 | SmfEvent* event = track->firstEvent; 471 | 472 | result = true; 473 | while(event) 474 | { 475 | if((event->port != prevEventPort) && (event->data[0] != SMF_EVENT_META)) 476 | { 477 | byte portChangeMessage[] = { 0xff, 0x21, 0x01, 0 }; 478 | SmfEvent* portChangeEvent; 479 | 480 | portChangeMessage[3] = (byte) event->port; 481 | portChangeEvent = smfEventCreate(event->time, event->port, 482 | portChangeMessage, sizeof(portChangeMessage)); 483 | if(!portChangeEvent) 484 | { 485 | result = false; 486 | break; 487 | } 488 | if(!eventProc(portChangeEvent, customData)) 489 | { 490 | result = false; 491 | break; 492 | } 493 | smfEventDelete(portChangeEvent); 494 | prevEventPort = event->port; 495 | } 496 | 497 | if(!eventProc(event, customData)) 498 | { 499 | result = false; 500 | break; 501 | } 502 | event = event->nextEvent; 503 | } 504 | } 505 | return result; 506 | } 507 | 508 | int smfTrackGetEndTiming(SmfTrack* track) 509 | { 510 | int endTiming = 0; 511 | 512 | if(track) 513 | { 514 | SmfEvent* endOfTrack = track->lastEvent; 515 | 516 | endTiming = endOfTrack->time; 517 | } 518 | return endTiming; 519 | } 520 | 521 | int smfTrackSetEndTiming(SmfTrack* track, int newEndTiming) 522 | { 523 | int oldEndTiming = 0; 524 | 525 | if(track) 526 | { 527 | SmfEvent* endOfTrack = track->lastEvent; 528 | int lastEventTiming = endOfTrack->prevEvent 529 | ? endOfTrack->prevEvent->time : 0; 530 | 531 | if(newEndTiming >= lastEventTiming) 532 | { 533 | endOfTrack->time = newEndTiming; 534 | } 535 | } 536 | return oldEndTiming; 537 | } 538 | 539 | 540 | bool smfReallocTrack(Smf* seq, int newNumTracks); 541 | 542 | Smf* smfCreate(void) 543 | { 544 | Smf* newSeq = (Smf*) calloc(1, sizeof(Smf)); 545 | 546 | if(newSeq) 547 | { 548 | newSeq->track = (SmfTrack**) malloc(sizeof(SmfTrack*)); 549 | if(newSeq->track) 550 | { 551 | newSeq->track[0] = smfTrackCreate(); 552 | if(newSeq->track[0]) 553 | { 554 | newSeq->numTracks++; 555 | } 556 | else 557 | { 558 | free(newSeq->track); 559 | free(newSeq); 560 | newSeq = NULL; 561 | } 562 | } 563 | else 564 | { 565 | free(newSeq); 566 | newSeq = NULL; 567 | } 568 | } 569 | 570 | return newSeq; 571 | } 572 | 573 | void smfDelete(Smf* seq) 574 | { 575 | if(seq) 576 | { 577 | int trackIndex; 578 | 579 | for(trackIndex = 0; trackIndex < seq->numTracks; trackIndex++) 580 | { 581 | smfTrackDelete(seq->track[trackIndex]); 582 | } 583 | free(seq); 584 | } 585 | } 586 | 587 | Smf* smfCopy(Smf* seq) 588 | { 589 | Smf* newSeq = smfCreate(); 590 | 591 | if(newSeq) 592 | { 593 | if(smfReallocTrack(newSeq, seq->numTracks)) 594 | { 595 | int trackIndex; 596 | 597 | for(trackIndex = 0; trackIndex < seq->numTracks; trackIndex++) 598 | { 599 | SmfTrack* newTrack = smfTrackCopy(seq->track[trackIndex]); 600 | if(!newTrack) 601 | { 602 | smfDelete(newSeq); 603 | newSeq = NULL; 604 | break; 605 | } 606 | smfTrackDelete(newSeq->track[trackIndex]); 607 | newSeq->track[trackIndex] = newTrack; 608 | } 609 | 610 | smfSetTimebase(newSeq, seq->timebase); 611 | } 612 | else 613 | { 614 | smfDelete(newSeq); 615 | newSeq = NULL; 616 | } 617 | } 618 | return newSeq; 619 | } 620 | 621 | bool smfInsertEvent(Smf* seq, int time, int port, int track, const byte* data, size_t dataSize) 622 | { 623 | bool result = false; 624 | 625 | if(seq) 626 | { 627 | bool allocResult = true; 628 | 629 | if(track >= seq->numTracks) 630 | { 631 | allocResult = smfReallocTrack(seq, track + 1); 632 | } 633 | if(allocResult) 634 | { 635 | result = smfTrackInsertEvent(seq->track[track], time, port, data, dataSize); 636 | } 637 | } 638 | return result; 639 | } 640 | 641 | size_t smfGetSize(Smf* seq) 642 | { 643 | size_t seqSize = 0; 644 | 645 | if(seq) 646 | { 647 | int trackIndex; 648 | 649 | seqSize = SMF_MTHD_SIZE; 650 | for(trackIndex = 0; trackIndex < seq->numTracks; trackIndex++) 651 | { 652 | seqSize += smfTrackGetSize(seq->track[trackIndex]); 653 | } 654 | } 655 | return seqSize; 656 | } 657 | 658 | size_t smfWrite(Smf* seq, byte* buffer, size_t bufferSize) 659 | { 660 | size_t transferedSize = 0; 661 | 662 | if(seq && buffer && bufferSize) 663 | { 664 | byte MThdData[SMF_MTHD_SIZE] = { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 0, 0, 0 }; 665 | 666 | smfWriteByte(2, seq->numTracks, &MThdData[10], 2); 667 | smfWriteByte(2, seq->timebase, &MThdData[12], 2); 668 | if(bufferSize >= SMF_MTHD_SIZE) 669 | { 670 | int trackIndex; 671 | 672 | memcpy(&buffer[transferedSize], MThdData, SMF_MTHD_SIZE); 673 | transferedSize += SMF_MTHD_SIZE; 674 | 675 | for(trackIndex = 0; trackIndex < seq->numTracks; trackIndex++) 676 | { 677 | transferedSize += smfTrackWrite(seq->track[trackIndex], 678 | &buffer[transferedSize], bufferSize - transferedSize); 679 | if(transferedSize == bufferSize) 680 | { 681 | break; 682 | } 683 | } 684 | } 685 | else 686 | { 687 | memcpy(&buffer[transferedSize], MThdData, bufferSize - transferedSize); 688 | transferedSize = bufferSize; 689 | } 690 | } 691 | return transferedSize; 692 | } 693 | 694 | int smfSetTimebase(Smf* seq, int newTimebase) 695 | { 696 | int oldTimebase = 0; 697 | 698 | if(seq && (newTimebase >= 0) && (newTimebase <= SMF_TIMEBASE_MAX)) 699 | { 700 | oldTimebase = seq->timebase; 701 | seq->timebase = newTimebase; 702 | } 703 | return oldTimebase; 704 | } 705 | 706 | int smfSetEndTimingOfTrack(Smf* seq, int track, int newEndTiming) 707 | { 708 | int oldEndTiming = 0; 709 | 710 | if(seq) 711 | { 712 | bool allocResult = true; 713 | 714 | if(track >= seq->numTracks) 715 | { 716 | allocResult = smfReallocTrack(seq, track + 1); 717 | } 718 | if(allocResult) 719 | { 720 | oldEndTiming = smfTrackSetEndTiming(seq->track[track], newEndTiming); 721 | } 722 | } 723 | return oldEndTiming; 724 | } 725 | 726 | bool smfReallocTrack(Smf* seq, int newNumTracks) 727 | { 728 | bool result = false; 729 | 730 | if(seq) 731 | { 732 | result = true; 733 | if(newNumTracks > seq->numTracks) 734 | { 735 | SmfTrack** newTracks = (SmfTrack**) realloc(seq->track, sizeof(SmfTrack*) * newNumTracks); 736 | 737 | if(newTracks) 738 | { 739 | int trackIndex; 740 | 741 | seq->track = newTracks; 742 | for(trackIndex = seq->numTracks; trackIndex < newNumTracks; trackIndex++) 743 | { 744 | seq->track[trackIndex] = smfTrackCreate(); 745 | seq->numTracks++; 746 | if(!seq->track[trackIndex]) 747 | { 748 | for(; trackIndex >= seq->numTracks; trackIndex--) 749 | { 750 | smfTrackDelete(seq->track[trackIndex]); 751 | seq->numTracks--; 752 | } 753 | result = false; 754 | break; 755 | } 756 | } 757 | } 758 | else 759 | { 760 | result = false; 761 | } 762 | } 763 | } 764 | return result; 765 | } 766 | -------------------------------------------------------------------------------- /src/decoder/libsmfc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * libsmfc.h: simple standard midi writer by loveemu 3 | */ 4 | 5 | 6 | #ifndef LIBSMFC_H 7 | #define LIBSMFC_H 8 | 9 | #include 10 | 11 | #if !defined(bool) && !defined(__cplusplus) 12 | typedef int bool; 13 | #define true 1 14 | #define false 0 15 | #endif /* !bool */ 16 | 17 | #ifndef byte 18 | typedef unsigned char byte; 19 | #endif /* !byte */ 20 | #ifndef sbyte 21 | typedef signed char sbyte; 22 | #endif /* !sbyte */ 23 | 24 | unsigned int smfReadVarLength(byte* buffer, size_t bufferSize); 25 | size_t smfWriteByte(size_t sizeToTransfer, unsigned int value, byte* buffer, size_t bufferSize); 26 | size_t smfGetVarLengthSize(unsigned int value); 27 | size_t smfWriteVarLength(unsigned int value, byte* buffer, size_t bufferSize); 28 | 29 | 30 | typedef struct TagSmfEvent SmfEvent; 31 | struct TagSmfEvent 32 | { 33 | byte* data; 34 | size_t size; 35 | int time; 36 | int port; 37 | SmfEvent* prevEvent; 38 | SmfEvent* nextEvent; 39 | }; 40 | 41 | SmfEvent* smfEventCreate(int time, int port, const byte* data, size_t dataSize); 42 | void smfEventDelete(SmfEvent* event); 43 | SmfEvent* smfEventCopy(SmfEvent* event); 44 | size_t smfEventGetSize(SmfEvent* event); 45 | size_t smfEventWrite(SmfEvent* event, byte* buffer, size_t bufferSize); 46 | int smfEventCompare(SmfEvent* event, SmfEvent* targetEvent); 47 | 48 | 49 | typedef struct TagSmfTrack 50 | { 51 | SmfEvent* firstEvent; 52 | SmfEvent* lastEvent; 53 | } SmfTrack; 54 | 55 | SmfTrack* smfTrackCreate(void); 56 | void smfTrackDelete(SmfTrack* track); 57 | SmfTrack* smfTrackCopy(SmfTrack* track); 58 | bool smfTrackInsertEvent(SmfTrack* track, int time, int port, const byte* data, size_t dataSize); 59 | size_t smfTrackGetSize(SmfTrack* track); 60 | size_t smfTrackWrite(SmfTrack* track, byte* buffer, size_t bufferSize); 61 | int smfTrackGetEndTiming(SmfTrack* track); 62 | int smfTrackSetEndTiming(SmfTrack* track, int newEndTiming); 63 | 64 | 65 | typedef struct TagSmf 66 | { 67 | int numTracks; 68 | int timebase; 69 | SmfTrack** track; 70 | } Smf; 71 | 72 | Smf* smfCreate(void); 73 | void smfDelete(Smf* seq); 74 | Smf* smfCopy(Smf* seq); 75 | bool smfInsertEvent(Smf* seq, int time, int port, int track, const byte* data, size_t dataSize); 76 | size_t smfGetSize(Smf* seq); 77 | size_t smfWrite(Smf* seq, byte* buffer, size_t bufferSize); 78 | int smfSetTimebase(Smf* seq, int newTimebase); 79 | int smfSetEndTimingOfTrack(Smf* seq, int track, int newEndTiming); 80 | 81 | #endif /* !LIBSMFC_H */ 82 | -------------------------------------------------------------------------------- /src/decoder/libsmfcx.c: -------------------------------------------------------------------------------- 1 | /** 2 | * libsmfcx.c: libsmfc extra functions by loveemu 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "libsmfc.h" 12 | #include "libsmfcx.h" 13 | 14 | #define SMF_EVENT_NOTEOFF 0x80 15 | #define SMF_EVENT_NOTEON 0x90 16 | #define SMF_EVENT_KEYPRESS 0xa0 17 | #define SMF_EVENT_CONTROL 0xb0 18 | #define SMF_EVENT_PROGRAM 0xc0 19 | #define SMF_EVENT_CHANPRESS 0xd0 20 | #define SMF_EVENT_PITCHBEND 0xe0 21 | #define SMF_EVENT_SYSEX 0xf0 22 | #define SMF_EVENT_SYSEXLITE 0xf7 23 | #define SMF_EVENT_META 0xff 24 | 25 | bool smfWriteFile(Smf* seq, const char* filename) 26 | { 27 | bool result = false; 28 | size_t seqSize = smfGetSize(seq); 29 | FILE* fileWriter = fopen(filename, "wb"); 30 | 31 | if(fileWriter) 32 | { 33 | byte* buffer = (byte*) malloc(seqSize); 34 | 35 | if(buffer) 36 | { 37 | smfWrite(seq, buffer, seqSize); 38 | result = (bool) fwrite(buffer, seqSize, 1, fileWriter); 39 | free(buffer); 40 | } 41 | fclose(fileWriter); 42 | } 43 | return result; 44 | } 45 | 46 | bool smfInsertNoteOff(Smf* seq, int time, int channel, int track, int key, int velocity) 47 | { 48 | bool result = false; 49 | 50 | if((key >= 0) && (key <= 127) && (velocity >= 0) && (velocity <= 127)) 51 | { 52 | byte noteOff[3]; 53 | 54 | noteOff[0] = SMF_EVENT_NOTEOFF | (channel % 16); 55 | noteOff[1] = key; 56 | noteOff[2] = velocity; 57 | result = smfInsertEvent(seq, time, channel / 16, track, noteOff, sizeof(noteOff)); 58 | } 59 | return result; 60 | } 61 | 62 | bool smfInsertNoteOn(Smf* seq, int time, int channel, int track, int key, int velocity) 63 | { 64 | bool result = false; 65 | 66 | if((key >= 0) && (key <= 127) && (velocity >= 0) && (velocity <= 127)) 67 | { 68 | byte noteOn[3]; 69 | 70 | noteOn[0] = SMF_EVENT_NOTEON | (channel % 16); 71 | noteOn[1] = key; 72 | noteOn[2] = velocity; 73 | result = smfInsertEvent(seq, time, channel / 16, track, noteOn, sizeof(noteOn)); 74 | } 75 | return result; 76 | } 77 | 78 | bool smfInsertNote(Smf* seq, int time, int channel, int track, int key, int velocity, int duration) 79 | { 80 | bool result = false; 81 | 82 | if(velocity > 0) 83 | { 84 | result = smfInsertNoteOn(seq, time, channel, track, key, velocity) 85 | && smfInsertNoteOn(seq, time + duration, channel, track, key, 0); 86 | } 87 | return result; 88 | } 89 | 90 | bool smfInsertKeyPress(Smf* seq, int time, int channel, int track, int key, int amount) 91 | { 92 | bool result = false; 93 | 94 | if((key >= 0) && (key <= 127) && (amount >= 0) && (amount <= 127)) 95 | { 96 | byte keyPress[3]; 97 | 98 | keyPress[0] = SMF_EVENT_KEYPRESS | (channel % 16); 99 | keyPress[1] = key; 100 | keyPress[2] = amount; 101 | result = smfInsertEvent(seq, time, channel / 16, track, keyPress, sizeof(keyPress)); 102 | } 103 | return result; 104 | } 105 | 106 | bool smfInsertControl(Smf* seq, int time, int channel, int track, int controlNumber, int value) 107 | { 108 | bool result = false; 109 | 110 | if((controlNumber >= 0) && (controlNumber <= 127) && (value >= 0) && (value <= 127)) 111 | { 112 | byte controlChange[3]; 113 | 114 | controlChange[0] = SMF_EVENT_CONTROL | (channel % 16); 115 | controlChange[1] = controlNumber; 116 | controlChange[2] = value; 117 | result = smfInsertEvent(seq, time, channel / 16, track, controlChange, sizeof(controlChange)); 118 | } 119 | return result; 120 | } 121 | 122 | bool smfInsertProgram(Smf* seq, int time, int channel, int track, int programNumber) 123 | { 124 | bool result = false; 125 | 126 | if((programNumber >= 0) && (programNumber <= 127)) 127 | { 128 | byte programChange[2]; 129 | 130 | programChange[0] = SMF_EVENT_PROGRAM | (channel % 16); 131 | programChange[1] = programNumber; 132 | result = smfInsertEvent(seq, time, channel / 16, track, programChange, sizeof(programChange)); 133 | } 134 | return result; 135 | } 136 | 137 | bool smfInsertChanPress(Smf* seq, int time, int channel, int track, int key, int amount) 138 | { 139 | bool result = false; 140 | 141 | if((key >= 0) && (key <= 127) && (amount >= 0) && (amount <= 127)) 142 | { 143 | byte chanPress[3]; 144 | 145 | chanPress[0] = SMF_EVENT_CHANPRESS | (channel % 16); 146 | chanPress[1] = key; 147 | chanPress[2] = amount; 148 | result = smfInsertEvent(seq, time, channel / 16, track, chanPress, sizeof(chanPress)); 149 | } 150 | return result; 151 | } 152 | 153 | bool smfInsertPitchBend(Smf* seq, int time, int channel, int track, int value) 154 | { 155 | bool result = false; 156 | 157 | if((value >= -8192) && (value <= 8191)) 158 | { 159 | byte pitchBend[3]; 160 | 161 | value += 8192; 162 | pitchBend[0] = SMF_EVENT_PITCHBEND | (channel % 16); 163 | pitchBend[1] = (unsigned int) value & 0x7f; 164 | pitchBend[2] = (unsigned int) value >> 7; 165 | result = smfInsertEvent(seq, time, channel / 16, track, pitchBend, sizeof(pitchBend)); 166 | } 167 | return result; 168 | } 169 | 170 | bool smfInsertSysex(Smf* seq, int time, int port, int track, const byte* data, size_t dataSize) 171 | { 172 | bool result = false; 173 | 174 | if(data && dataSize && ((data[0] == SMF_EVENT_SYSEX) || (data[0] == SMF_EVENT_SYSEXLITE))) 175 | { 176 | size_t sysexLength = dataSize - 1; 177 | size_t sysexLengthSize = smfGetVarLengthSize((unsigned int) sysexLength); 178 | size_t sysexDataSize = 1 + sysexLengthSize + sysexLength; 179 | byte* sysexData = (byte*) malloc(sysexDataSize); 180 | 181 | if(sysexData) 182 | { 183 | sysexData[0] = data[0]; 184 | smfWriteVarLength((unsigned int) sysexLength, &sysexData[1], sysexLength); 185 | memcpy(&sysexData[1 + sysexLengthSize], &data[1], sysexLength); 186 | result = smfInsertEvent(seq, time, port, track, sysexData, sysexDataSize); 187 | free(sysexData); 188 | } 189 | } 190 | return result; 191 | } 192 | 193 | bool smfInsertMetaEvent(Smf* seq, int time, int track, int metaType, const byte* data, size_t dataSize) 194 | { 195 | bool result = false; 196 | 197 | if((metaType >= 0) && (metaType <= 255) && data && dataSize) 198 | { 199 | size_t metaLength = dataSize; 200 | size_t metaLengthSize = smfGetVarLengthSize((unsigned int) metaLength); 201 | size_t metaDataSize = 2 + metaLengthSize + metaLength; 202 | byte* metaData = (byte*) malloc(metaDataSize); 203 | 204 | if(metaData) 205 | { 206 | metaData[0] = SMF_EVENT_META; 207 | metaData[1] = metaType; 208 | smfWriteVarLength((unsigned int) metaLength, &metaData[2], metaLength); 209 | memcpy(&metaData[2 + metaLengthSize], data, metaLength); 210 | result = smfInsertEvent(seq, time, 0, track, metaData, metaDataSize); 211 | free(metaData); 212 | } 213 | } 214 | return result; 215 | } 216 | 217 | bool smfInsertMetaText(Smf* seq, int time, int track, int metaType, const char* text) 218 | { 219 | bool result = false; 220 | 221 | if(text) 222 | { 223 | result = smfInsertMetaEvent(seq, time, track, metaType, text, strlen(text)); 224 | } 225 | return result; 226 | } 227 | 228 | bool smfInsertGM1SystemOn(Smf* seq, int time, int port, int track) 229 | { 230 | byte sysexGM1SystemOn[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 }; 231 | 232 | return smfInsertSysex(seq, time, port, track, 233 | sysexGM1SystemOn, sizeof(sysexGM1SystemOn)); 234 | } 235 | 236 | bool smfInsertMasterVolume(Smf* seq, int time, int port, int track, int volume) 237 | { 238 | bool result = false; 239 | 240 | if((volume >= 0) && (volume <= 127)) 241 | { 242 | byte sysexMasterVolume[] = { 0xF0, 0x7F, 0x7F, 0x04, 0x01, 0x00, (byte) volume, 0xF7 }; 243 | 244 | result = smfInsertSysex(seq, time, port, track, sysexMasterVolume, sizeof(sysexMasterVolume)); 245 | } 246 | return result; 247 | } 248 | 249 | bool smfInsertTempo(Smf* seq, int time, int track, int microSeconds) 250 | { 251 | bool result = false; 252 | 253 | if((microSeconds >= 0) && (microSeconds <= 0xffffff)) 254 | { 255 | byte metaTempo[3]; 256 | 257 | smfWriteByte(3, microSeconds, metaTempo, 3); 258 | result = smfInsertMetaEvent(seq, time, track, SMF_META_SETTEMPO, metaTempo, sizeof(metaTempo)); 259 | } 260 | return result; 261 | } 262 | 263 | bool smfInsertTempoBPM(Smf* seq, int time, int track, double bpm) 264 | { 265 | double microSeconds = 60000000 / bpm; 266 | 267 | return smfInsertTempo(seq, time, track, (int) microSeconds); 268 | } 269 | -------------------------------------------------------------------------------- /src/decoder/libsmfcx.h: -------------------------------------------------------------------------------- 1 | /** 2 | * libsmfcx.h: libsmfc extra functions by loveemu 3 | */ 4 | 5 | 6 | #ifndef LIBSMFCX_H 7 | #define LIBSMFCX_H 8 | 9 | #include "libsmfc.h" 10 | 11 | #define SMF_CONTROL_BANKSELM 0 12 | #define SMF_CONTROL_MODULATION 1 13 | #define SMF_CONTROL_PORTAMENTOTIME 5 14 | #define SMF_CONTROL_DATAENTRYM 6 15 | #define SMF_CONTROL_VOLUME 7 16 | #define SMF_CONTROL_PANPOT 10 17 | #define SMF_CONTROL_EXPRESSION 11 18 | #define SMF_CONTROL_BANKSELL 32 19 | #define SMF_CONTROL_DATAENTRYL 38 20 | #define SMF_CONTROL_PORTAMENTO 65 21 | #define SMF_CONTROL_PORTAMENTOCTRL 84 22 | #define SMF_CONTROL_TIMBRE 71 23 | #define SMF_CONTROL_RELEASETIME 72 24 | #define SMF_CONTROL_ATTACKTIME 73 25 | #define SMF_CONTROL_BRIGHTNESS 74 26 | #define SMF_CONTROL_DECAYTIME 75 27 | #define SMF_CONTROL_VIBRATORATE 76 28 | #define SMF_CONTROL_VIBRATODEPTH 77 29 | #define SMF_CONTROL_VIBRATODELAY 78 30 | #define SMF_CONTROL_REVERB 91 31 | #define SMF_CONTROL_CHORUS 93 32 | #define SMF_CONTROL_NRPNL 98 33 | #define SMF_CONTROL_NRPNM 99 34 | #define SMF_CONTROL_RPNL 100 35 | #define SMF_CONTROL_RPNM 101 36 | #define SMF_CONTROL_MONO 126 37 | #define SMF_CONTROL_POLY 127 38 | 39 | #define SMF_META_TEXT 0x01 40 | #define SMF_META_COPYRIGHT 0x02 41 | #define SMF_META_TRACKNAME 0x03 42 | #define SMF_META_SEQUENCENAME 0x03 43 | #define SMF_META_SETTEMPO 0x51 44 | 45 | bool smfWriteFile(Smf* seq, const char* filename); 46 | bool smfInsertNoteOff(Smf* seq, int time, int channel, int track, int key, int velocity); 47 | bool smfInsertNoteOn(Smf* seq, int time, int channel, int track, int key, int velocity); 48 | bool smfInsertNote(Smf* seq, int time, int channel, int track, int key, int velocity, int duration); 49 | bool smfInsertKeyPress(Smf* seq, int time, int channel, int track, int key, int amount); 50 | bool smfInsertControl(Smf* seq, int time, int channel, int track, int controlNumber, int value); 51 | bool smfInsertProgram(Smf* seq, int time, int channel, int track, int programNumber); 52 | bool smfInsertChanPress(Smf* seq, int time, int channel, int track, int key, int amount); 53 | bool smfInsertPitchBend(Smf* seq, int time, int channel, int track, int value); 54 | bool smfInsertSysex(Smf* seq, int time, int port, int track, const byte* data, size_t dataSize); 55 | bool smfInsertMetaEvent(Smf* seq, int time, int track, int metaType, const byte* data, size_t dataSize); 56 | bool smfInsertMetaText(Smf* seq, int time, int track, int metaType, const char* text); 57 | 58 | bool smfInsertGM1SystemOn(Smf* seq, int time, int port, int track); 59 | bool smfInsertMasterVolume(Smf* seq, int time, int port, int track, int volume); 60 | bool smfInsertTempo(Smf* seq, int time, int track, int microSeconds); 61 | bool smfInsertTempoBPM(Smf* seq, int time, int track, double bpm); 62 | 63 | #endif /* !LIBSMFCX_H */ 64 | -------------------------------------------------------------------------------- /src/decoder/nssamp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * nssamp.c: nds sample decoder 3 | * written by loveemu, feel free to redistribute 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include "cioutil.h" 11 | #include "nssamp.h" 12 | 13 | 14 | #define WAVE_HEADER_SIZE 0x2c 15 | 16 | 17 | typedef struct TagNSSampDecBlockInfo 18 | { 19 | size_t transferedSize; 20 | NSSampADPCMInfo adpcm; 21 | } NSSampDecBlockInfo; 22 | 23 | 24 | void clamp_step_index(int* stepIndex); 25 | void clamp_sample(int* decompSample); 26 | void process_nibble(unsigned char code, int* stepIndex, int* decompSample); 27 | 28 | 29 | int nsSampGetBPSFromWaveType(int waveType) 30 | { 31 | return (waveType == NSSAMP_WAVE_PCM8) ? 8 : 16; 32 | } 33 | 34 | bool nsSampDecodeBlock(byte* dest, const byte* blocks, size_t blockSize, int nSamples, int waveType, int channels) 35 | { 36 | bool result = false; 37 | NSSampDecBlockInfo* info; 38 | 39 | info = (NSSampDecBlockInfo*) calloc(channels, sizeof(NSSampDecBlockInfo)); 40 | if(info) 41 | { 42 | int ch; 43 | int sampId; 44 | size_t blockHead; 45 | size_t transferedSize; 46 | size_t destOfs = 0; 47 | int bps = nsSampGetBPSFromWaveType(waveType); 48 | 49 | blockHead = 0; 50 | for(ch = 0; ch < channels; ch++) 51 | { 52 | switch(waveType) 53 | { 54 | case NSSAMP_WAVE_ADPCM: 55 | info[ch].transferedSize = 4; 56 | info[ch].adpcm.low = true; 57 | info[ch].adpcm.samp = utos2(mget2l(&blocks[blockHead])); 58 | info[ch].adpcm.stepIndex = mget1(&blocks[blockHead + 2]); 59 | break; 60 | } 61 | blockHead += blockSize; 62 | } 63 | 64 | for(sampId = 0; sampId < nSamples; sampId++) 65 | { 66 | blockHead = 0; 67 | for(ch = 0; ch < channels; ch++) 68 | { 69 | transferedSize = info[ch].transferedSize; 70 | 71 | transferedSize += nsSampDecode(&dest[destOfs], 72 | &blocks[blockHead + transferedSize], waveType, &info[ch].adpcm); 73 | destOfs += bps/8; 74 | 75 | info[ch].transferedSize = transferedSize; 76 | blockHead += blockSize; 77 | } 78 | } 79 | 80 | result = true; 81 | free(info); 82 | } 83 | return result; 84 | } 85 | 86 | size_t nsSampDecode(byte* dest, const byte* src, int waveType, NSSampADPCMInfo* adpcm) 87 | { 88 | size_t transferedSize = 0; 89 | 90 | switch(waveType) 91 | { 92 | case NSSAMP_WAVE_PCM8: 93 | dest[0] = src[0] ^ 0x80; 94 | transferedSize++; 95 | break; 96 | 97 | case NSSAMP_WAVE_PCM16: 98 | memcpy(dest, src, 2); 99 | transferedSize += 2; 100 | break; 101 | 102 | case NSSAMP_WAVE_ADPCM: 103 | { 104 | bool low = adpcm->low; 105 | byte code = src[0]; 106 | 107 | if(!low) 108 | { 109 | code = code >> 4; 110 | transferedSize++; 111 | } 112 | process_nibble(code, &adpcm->stepIndex, &adpcm->samp); 113 | mput2l(adpcm->samp, dest); 114 | adpcm->low = !low; 115 | break; 116 | } 117 | } 118 | 119 | return transferedSize; 120 | } 121 | 122 | 123 | void clamp_step_index(int* stepIndex) 124 | { 125 | if (*stepIndex < 0 ) *stepIndex = 0; 126 | if (*stepIndex > 88) *stepIndex = 88; 127 | } 128 | 129 | void clamp_sample(int* decompSample) 130 | { 131 | if (*decompSample < -32768) *decompSample = -32768; 132 | if (*decompSample > 32767) *decompSample = 32767; 133 | } 134 | 135 | void process_nibble(unsigned char code, int* stepIndex, int* decompSample) 136 | { 137 | const unsigned ADPCMTable[89] = 138 | { 139 | 7, 8, 9, 10, 11, 12, 13, 14, 140 | 16, 17, 19, 21, 23, 25, 28, 31, 141 | 34, 37, 41, 45, 50, 55, 60, 66, 142 | 73, 80, 88, 97, 107, 118, 130, 143, 143 | 157, 173, 190, 209, 230, 253, 279, 307, 144 | 337, 371, 408, 449, 494, 544, 598, 658, 145 | 724, 796, 876, 963, 1060, 1166, 1282, 1411, 146 | 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 147 | 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 148 | 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 149 | 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 150 | 32767 151 | }; 152 | 153 | const int IMA_IndexTable[16] = 154 | { 155 | -1, -1, -1, -1, 2, 4, 6, 8, 156 | -1, -1, -1, -1, 2, 4, 6, 8 157 | }; 158 | 159 | unsigned step; 160 | int diff; 161 | 162 | code &= 0x0F; 163 | 164 | step = ADPCMTable[*stepIndex]; 165 | diff = step >> 3; 166 | if (code & 1) diff += step >> 2; 167 | if (code & 2) diff += step >> 1; 168 | if (code & 4) diff += step; 169 | // Windows: 170 | //if (code & 8) *decompSample -= diff; 171 | //else *decompSample += diff; 172 | //if (*decompSample < -32768) *decompSample = -32768; 173 | //if (*decompSample > 32767) *decompSample = 32767; 174 | // Nitro: (has minor clipping-error, see GBATEK for details) 175 | if (code & 8) { 176 | *decompSample -= diff; 177 | if (*decompSample < -32767) 178 | *decompSample = -32767; 179 | } 180 | else { 181 | *decompSample += diff; 182 | if (*decompSample > 32767) 183 | *decompSample = 32767; 184 | } 185 | (*stepIndex) += IMA_IndexTable[code]; 186 | clamp_step_index(stepIndex); 187 | } 188 | -------------------------------------------------------------------------------- /src/decoder/nssamp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * nssamp.h: nds sample decoder 3 | * written by loveemu, feel free to redistribute 4 | */ 5 | 6 | 7 | #ifndef NSSAMP_H 8 | #define NSSAMP_H 9 | 10 | 11 | #include "cioutil.h" 12 | 13 | 14 | #define NSSAMP_WAVE_PCM8 0 15 | #define NSSAMP_WAVE_PCM16 1 16 | #define NSSAMP_WAVE_ADPCM 2 17 | 18 | typedef struct TagNSSampADPCMInfo 19 | { 20 | bool low; 21 | int samp; 22 | int stepIndex; 23 | } NSSampADPCMInfo; 24 | 25 | int nsSampGetBPSFromWaveType(int waveType); 26 | bool nsSampDecodeBlock(byte* dest, const byte* blocks, size_t blockSize, int nSamples, int waveType, int channels); 27 | size_t nsSampDecode(byte* dest, const byte* src, int waveType, NSSampADPCMInfo* adpcm); 28 | 29 | 30 | #endif /* !NSSAMP_H */ 31 | -------------------------------------------------------------------------------- /src/decoder/nsstrm.c: -------------------------------------------------------------------------------- 1 | /** 2 | * nsstrm.c: nds strm functions 3 | * written by loveemu, feel free to redistribute 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include "cioutil.h" 11 | #include "nssamp.h" 12 | #include "nsstrm.h" 13 | 14 | 15 | #define WAVE_HEADER_SIZE 0x2c 16 | 17 | NSStrm* nsStrmCreate(const byte* strm, size_t size) 18 | { 19 | NSStrm* newStrm = NULL; 20 | 21 | if(size >= 0x68 22 | && mget4l(&strm[0x00]) == 0x4d525453 /* STRM */ 23 | && mget2l(&strm[0x04]) == 0xfeff 24 | && mget2l(&strm[0x0c]) == 0x0010 25 | && mget4l(&strm[0x10]) == 0x44414548 /* HEAD */ 26 | ) 27 | { 28 | int channels = mget1(&strm[0x1a]); 29 | int numBlocks = mget4l(&strm[0x2c]); 30 | 31 | if(channels > 0 && numBlocks > 0) 32 | { 33 | size_t dataOffset = mget4l(&strm[0x28]); 34 | size_t lenBlockPerChan = mget4l(&strm[0x30]); 35 | size_t lenLastBlockPerChan = mget4l(&strm[0x38]); 36 | size_t lenBlock = lenBlockPerChan * channels; 37 | size_t lenLastBlock = lenLastBlockPerChan * channels; 38 | size_t dataSize = lenBlock * (numBlocks-1) + lenLastBlock; 39 | byte* data; 40 | 41 | if(dataOffset + dataSize <= size) 42 | { 43 | data = (byte*) malloc(dataSize); 44 | if(data) 45 | { 46 | newStrm = (NSStrm*) calloc(1, sizeof(NSStrm)); 47 | if(newStrm) 48 | { 49 | int waveType = mget1(&strm[0x18]); 50 | bool hasLoop = (bool) mget1(&strm[0x19]); 51 | int numSamp = mget4l(&strm[0x24]); 52 | int bps = nsSampGetBPSFromWaveType(waveType); 53 | 54 | memcpy(data, &strm[dataOffset], dataSize); 55 | 56 | newStrm->waveType = waveType; 57 | newStrm->hasLoop = hasLoop; 58 | newStrm->channels = channels; 59 | newStrm->rate = mget2l(&strm[0x1c]); 60 | newStrm->time = mget2l(&strm[0x1e]); 61 | newStrm->loopStart = hasLoop ? mget4l(&strm[0x20]) : 0; 62 | newStrm->numSamp = numSamp; 63 | newStrm->numBlocks = numBlocks; 64 | newStrm->lenBlock = lenBlockPerChan; 65 | newStrm->sampPerBlock = mget4l(&strm[0x34]); 66 | newStrm->lenLastBlock = lenLastBlockPerChan; 67 | newStrm->sampPerLastBlock = mget4l(&strm[0x3c]); 68 | newStrm->data = data; 69 | newStrm->dataSize = dataSize; 70 | 71 | newStrm->bps = bps; 72 | newStrm->decodedSampSize = numSamp * (bps/8) * channels; 73 | } 74 | else 75 | { 76 | free(data); 77 | } 78 | } 79 | } 80 | } 81 | } 82 | return newStrm; 83 | } 84 | 85 | void nsStrmDelete(NSStrm* strm) 86 | { 87 | if(strm) 88 | { 89 | free(strm->data); 90 | free(strm); 91 | } 92 | } 93 | 94 | NSStrm* nsStrmReadFile(const char* path) 95 | { 96 | NSStrm* newStrm = NULL; 97 | FILE* strmFile = fopen(path, "rb"); 98 | 99 | if(strmFile) 100 | { 101 | size_t strmFileSize; 102 | byte* strmBuf; 103 | 104 | fseek(strmFile, 0, SEEK_END); 105 | strmFileSize = (size_t) ftell(strmFile); 106 | rewind(strmFile); 107 | 108 | strmBuf = (byte*) malloc(strmFileSize); 109 | if(strmBuf) 110 | { 111 | size_t readSize; 112 | 113 | readSize = fread(strmBuf, 1, strmFileSize, strmFile); 114 | if(readSize == strmFileSize) 115 | { 116 | newStrm = nsStrmCreate(strmBuf, strmFileSize); 117 | } 118 | free(strmBuf); 119 | } 120 | fclose(strmFile); 121 | } 122 | return newStrm; 123 | } 124 | 125 | size_t nsStrmGetWaveSize(NSStrm* strm) 126 | { 127 | size_t waveSize = WAVE_HEADER_SIZE; 128 | 129 | if(strm) 130 | { 131 | waveSize += strm->decodedSampSize; 132 | } 133 | return waveSize; 134 | } 135 | 136 | bool nsStrmWriteToWave(NSStrm* strm, byte* buf, size_t bufSize) 137 | { 138 | bool result = true; 139 | size_t waveSize = nsStrmGetWaveSize(strm); 140 | 141 | if(strm && bufSize >= waveSize) 142 | { 143 | int waveType = strm->waveType; 144 | int channels = strm->channels; 145 | int rate = strm->rate; 146 | int bps = strm->bps; 147 | int decodedSampSize = strm->decodedSampSize; 148 | int numBlocks = strm->numBlocks; 149 | size_t lenBlock = strm->lenBlock; 150 | int sampPerBlock = strm->sampPerBlock; 151 | size_t lenLastBlock = strm->lenLastBlock; 152 | int sampPerLastBlock = strm->sampPerLastBlock; 153 | const byte* data = strm->data; 154 | size_t destOfs; 155 | size_t srcOfs; 156 | int blockId; 157 | 158 | mput4l(0x46464952, &buf[0x00]); /* RIFF */ 159 | mput4l((int) (waveSize - 8), &buf[0x04]); 160 | mput4l(0x45564157, &buf[0x08]); /* WAVE */ 161 | mput4l(0x20746d66, &buf[0x0c]); /* fmt */ 162 | mput4l(16, &buf[0x10]); 163 | mput2l(1, &buf[0x14]); 164 | mput2l(channels, &buf[0x16]); 165 | mput4l(rate, &buf[0x18]); 166 | mput4l(rate * channels * (bps/8), &buf[0x1c]); 167 | mput2l(channels * bps/8, &buf[0x20]); 168 | mput2l(bps, &buf[0x22]); 169 | mput4l(0x61746164, &buf[0x24]); /* data */ 170 | mput4l(decodedSampSize, &buf[0x28]); 171 | 172 | destOfs = WAVE_HEADER_SIZE; 173 | srcOfs = 0; 174 | for(blockId = 0; blockId < (numBlocks-1); blockId++) 175 | { 176 | nsSampDecodeBlock(&buf[destOfs], &data[srcOfs], lenBlock, sampPerBlock, waveType, channels); 177 | destOfs += sampPerBlock * (bps/8) * channels; 178 | srcOfs += lenBlock * channels; 179 | } 180 | nsSampDecodeBlock(&buf[destOfs], &data[srcOfs], lenLastBlock, sampPerLastBlock, waveType, channels); 181 | #if 0 182 | destOfs += sampPerLastBlock * (bps/8) * channels; 183 | srcOfs += lenLastBlock * channels; 184 | #endif 185 | } 186 | return result; 187 | } 188 | 189 | bool nsStrmWriteToWaveFile(NSStrm* strm, const char* path) 190 | { 191 | bool result = false; 192 | size_t waveSize; 193 | byte* wave; 194 | 195 | if(strm) 196 | { 197 | waveSize = nsStrmGetWaveSize(strm); 198 | wave = (byte*) malloc(waveSize); 199 | if(wave) 200 | { 201 | if(nsStrmWriteToWave(strm, wave, waveSize)) 202 | { 203 | FILE* waveFile = fopen(path, "wb"); 204 | 205 | if(waveFile) 206 | { 207 | size_t writtenSize; 208 | 209 | writtenSize = fwrite(wave, 1, waveSize, waveFile); 210 | if(writtenSize == waveSize) 211 | { 212 | result = true; 213 | } 214 | 215 | fclose(waveFile); 216 | } 217 | } 218 | free(wave); 219 | } 220 | } 221 | return result; 222 | } 223 | -------------------------------------------------------------------------------- /src/decoder/nsstrm.h: -------------------------------------------------------------------------------- 1 | /** 2 | * nsstrm.h: nds strm functions 3 | * written by loveemu, feel free to redistribute 4 | */ 5 | 6 | 7 | #ifndef NSSTRM_H 8 | #define NSSTRM_H 9 | 10 | 11 | #include "cioutil.h" 12 | 13 | 14 | typedef struct TagNSStrm 15 | { 16 | int waveType; 17 | bool hasLoop; 18 | int channels; 19 | int rate; 20 | int time; 21 | int loopStart; 22 | int numSamp; 23 | int numBlocks; 24 | size_t lenBlock; 25 | int sampPerBlock; 26 | size_t lenLastBlock; 27 | int sampPerLastBlock; 28 | int bps; 29 | int decodedSampSize; 30 | byte* data; 31 | size_t dataSize; 32 | } NSStrm; 33 | 34 | NSStrm* nsStrmCreate(const byte* strm, size_t size); 35 | void nsStrmDelete(NSStrm* strm); 36 | NSStrm* nsStrmReadFile(const char* path); 37 | size_t nsStrmGetWaveSize(NSStrm* strm); 38 | bool nsStrmWriteToWave(NSStrm* strm, byte* buf, size_t bufSize); 39 | bool nsStrmWriteToWaveFile(NSStrm* strm, const char* path); 40 | 41 | 42 | #endif /* !NSSTRM_H */ 43 | -------------------------------------------------------------------------------- /src/decoder/nsswav.c: -------------------------------------------------------------------------------- 1 | /** 2 | * nsstrm.c: nds strm functions 3 | * written by loveemu, feel free to redistribute 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include "cioutil.h" 11 | #include "nssamp.h" 12 | #include "nsswav.h" 13 | 14 | 15 | #define WAVE_HEADER_SIZE 0x2c 16 | 17 | NSSwav* nsSwavCreate(const byte* swav, size_t size) 18 | { 19 | NSSwav* newSwav = NULL; 20 | 21 | if(size >= 0x18 22 | && mget4l(&swav[0x00]) == 0x56415753 /* SWAV */ 23 | && mget2l(&swav[0x04]) == 0xfeff 24 | && mget2l(&swav[0x0c]) == 0x0010 25 | && mget4l(&swav[0x10]) == 0x41544144 /* DATA */ 26 | ) 27 | { 28 | newSwav = nsSwavCreateFromSamp(&swav[0x18], size - 0x18); 29 | } 30 | return newSwav; 31 | } 32 | 33 | NSSwav* nsSwavCreateFromSamp(const byte* sampHeader, size_t size) 34 | { 35 | NSSwav* newSwav = NULL; 36 | 37 | if(sampHeader && size >= 0x0c) 38 | { 39 | int waveType = mget1(&sampHeader[0x00]); 40 | bool hasLoop = (bool) mget1(&sampHeader[0x01]); 41 | int bps = nsSampGetBPSFromWaveType(waveType); 42 | int firstSampOfs = (waveType == NSSAMP_WAVE_ADPCM) ? 4 : 0; 43 | int byteToSampMul = (waveType == NSSAMP_WAVE_ADPCM) ? 2 : 1; 44 | int byteToSampDiv = (waveType == NSSAMP_WAVE_PCM16) ? 2 : 1; 45 | int loopStartInBytes = mget2l(&sampHeader[0x06]) * 4; 46 | int loopLenInBytes = mget4l(&sampHeader[0x08]) * 4; 47 | int loopStart = (loopStartInBytes - firstSampOfs) * byteToSampMul / byteToSampDiv; 48 | int loopLen = loopLenInBytes * byteToSampMul / byteToSampDiv; 49 | int numSamp = loopStart + loopLen; 50 | size_t dataOffset = 0x0c; 51 | size_t dataSize = loopStartInBytes + loopLenInBytes; 52 | byte* data; 53 | 54 | if(dataOffset + dataSize <= size) 55 | { 56 | data = (byte*) malloc(dataSize); 57 | if(data) 58 | { 59 | newSwav = (NSSwav*) calloc(1, sizeof(NSSwav)); 60 | if(newSwav) 61 | { 62 | memcpy(data, &sampHeader[dataOffset], dataSize); 63 | 64 | newSwav->waveType = waveType; 65 | newSwav->hasLoop = hasLoop; 66 | newSwav->rate = mget2l(&sampHeader[0x02]); 67 | newSwav->time = mget2l(&sampHeader[0x04]); 68 | newSwav->loopStart = hasLoop ? loopStart : 0; 69 | newSwav->numSamp = numSamp; 70 | newSwav->data = data; 71 | newSwav->dataSize = dataSize; 72 | 73 | newSwav->bps = bps; 74 | newSwav->decodedSampSize = numSamp * (bps/8); 75 | } 76 | else 77 | { 78 | free(data); 79 | } 80 | } 81 | } 82 | } 83 | return newSwav; 84 | } 85 | 86 | void nsSwavDelete(NSSwav* swav) 87 | { 88 | if(swav) 89 | { 90 | free(swav->data); 91 | free(swav); 92 | } 93 | } 94 | 95 | NSSwav* nsSwavReadFile(const char* path) 96 | { 97 | NSSwav* newSwav = NULL; 98 | FILE* swavFile = fopen(path, "rb"); 99 | 100 | if(swavFile) 101 | { 102 | size_t swavFileSize; 103 | byte* swavBuf; 104 | 105 | fseek(swavFile, 0, SEEK_END); 106 | swavFileSize = (size_t) ftell(swavFile); 107 | rewind(swavFile); 108 | 109 | swavBuf = (byte*) malloc(swavFileSize); 110 | if(swavBuf) 111 | { 112 | size_t readSize; 113 | 114 | readSize = fread(swavBuf, 1, swavFileSize, swavFile); 115 | if(readSize == swavFileSize) 116 | { 117 | newSwav = nsSwavCreate(swavBuf, swavFileSize); 118 | } 119 | free(swavBuf); 120 | } 121 | fclose(swavFile); 122 | } 123 | return newSwav; 124 | } 125 | 126 | size_t nsSwavGetWaveSize(NSSwav* swav) 127 | { 128 | size_t waveSize = WAVE_HEADER_SIZE; 129 | 130 | if(swav) 131 | { 132 | waveSize += swav->decodedSampSize; 133 | } 134 | return waveSize; 135 | } 136 | 137 | bool nsSwavWriteToWave(NSSwav* swav, byte* buf, size_t bufSize) 138 | { 139 | bool result = true; 140 | size_t waveSize = nsSwavGetWaveSize(swav); 141 | 142 | if(swav && bufSize >= waveSize) 143 | { 144 | int channels = 1; 145 | int rate = swav->rate; 146 | int bps = swav->bps; 147 | int decodedSampSize = swav->decodedSampSize; 148 | /* 149 | int waveType = swav->waveType; 150 | int sampPerBlock = swav->numSamp; 151 | size_t lenBlock = sampPerBlock; 152 | int numBlocks = swav->numBlocks; 153 | size_t lenLastBlock = strm->lenLastBlock; 154 | int sampPerLastBlock = strm->sampPerLastBlock; 155 | size_t destOfs; 156 | size_t srcOfs; 157 | int blockId; 158 | const byte* data = swav->data; 159 | */ 160 | 161 | mput4l(0x46464952, &buf[0x00]); /* RIFF */ 162 | mput4l((int) (waveSize - 8), &buf[0x04]); 163 | mput4l(0x45564157, &buf[0x08]); /* WAVE */ 164 | mput4l(0x20746d66, &buf[0x0c]); /* fmt */ 165 | mput4l(16, &buf[0x10]); 166 | mput2l(1, &buf[0x14]); 167 | mput2l(channels, &buf[0x16]); 168 | mput4l(rate, &buf[0x18]); 169 | mput4l(rate * channels * (bps/8), &buf[0x1c]); 170 | mput2l(channels * bps/8, &buf[0x20]); 171 | mput2l(bps, &buf[0x22]); 172 | mput4l(0x61746164, &buf[0x24]); /* data */ 173 | mput4l(decodedSampSize, &buf[0x28]); 174 | 175 | nsSampDecodeBlock(&buf[WAVE_HEADER_SIZE], swav->data, swav->dataSize, 176 | swav->numSamp, swav->waveType, channels); 177 | /* 178 | destOfs = WAVE_HEADER_SIZE; 179 | srcOfs = 0; 180 | for(blockId = 0; blockId < (numBlocks-1); blockId++) 181 | { 182 | nsSampDecodeBlock(&buf[destOfs], &data[srcOfs], lenBlock, sampPerBlock, waveType, channels); 183 | destOfs += sampPerBlock * (bps/8) * channels; 184 | srcOfs += lenBlock * channels; 185 | } 186 | nsSampDecodeBlock(&buf[destOfs], &data[srcOfs], lenLastBlock, sampPerLastBlock, waveType, channels); 187 | destOfs += sampPerLastBlock * (bps/8) * channels; 188 | srcOfs += lenLastBlock * channels; 189 | */ 190 | } 191 | return result; 192 | } 193 | 194 | bool nsSwavWriteToWaveFile(NSSwav* swav, const char* path) 195 | { 196 | bool result = false; 197 | size_t waveSize; 198 | byte* wave; 199 | 200 | if(swav) 201 | { 202 | waveSize = nsSwavGetWaveSize(swav); 203 | wave = (byte*) malloc(waveSize); 204 | if(wave) 205 | { 206 | if(nsSwavWriteToWave(swav, wave, waveSize)) 207 | { 208 | FILE* waveFile = fopen(path, "wb"); 209 | 210 | if(waveFile) 211 | { 212 | size_t writtenSize; 213 | 214 | writtenSize = fwrite(wave, 1, waveSize, waveFile); 215 | if(writtenSize == waveSize) 216 | { 217 | result = true; 218 | } 219 | 220 | fclose(waveFile); 221 | } 222 | } 223 | free(wave); 224 | } 225 | } 226 | return result; 227 | } 228 | -------------------------------------------------------------------------------- /src/decoder/nsswav.h: -------------------------------------------------------------------------------- 1 | /** 2 | * nsswav.h: nds swav functions 3 | * written by loveemu, feel free to redistribute 4 | */ 5 | 6 | 7 | #ifndef NSSWAV_H 8 | #define NSSWAV_H 9 | 10 | 11 | #include "cioutil.h" 12 | 13 | 14 | typedef struct TagNSSwav 15 | { 16 | int waveType; 17 | bool hasLoop; 18 | int rate; 19 | int time; 20 | int loopStart; 21 | int numSamp; 22 | int bps; 23 | int decodedSampSize; 24 | byte* data; 25 | size_t dataSize; 26 | } NSSwav; 27 | 28 | NSSwav* nsSwavCreate(const byte* swav, size_t size); 29 | NSSwav* nsSwavCreateFromSamp(const byte* sampHeader, size_t size); 30 | void nsSwavDelete(NSSwav* swav); 31 | NSSwav* nsSwavReadFile(const char* path); 32 | size_t nsSwavGetWaveSize(NSSwav* swav); 33 | bool nsSwavWriteToWave(NSSwav* swav, byte* buf, size_t bufSize); 34 | bool nsSwavWriteToWaveFile(NSSwav* swav, const char* path); 35 | 36 | 37 | #endif /* !NSSWAV_H */ 38 | -------------------------------------------------------------------------------- /src/decoder/sseq2mid.c: -------------------------------------------------------------------------------- 1 | /** 2 | * sseq2mid.c: convert sseq into standard midi 3 | * presented by loveemu, feel free to redistribute 4 | * see sseq2midConvert to know conversion detail :) 5 | * 6 | * 7 | * Minor change to remove the main function an all other unnessary functions. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "sseq2mid.h" 14 | 15 | 16 | #ifndef countof 17 | #define countof(a) (sizeof(a) / sizeof(a[0])) 18 | #endif 19 | 20 | #define SSEQ2MID_NAME "sseq2mid" 21 | #define SSEQ2MID_VER "20070314" 22 | 23 | bool g_log = false; 24 | bool g_modifyChOrder = false; 25 | bool g_noReverb = false; 26 | int g_loopCount = 1; 27 | int g_loopStyle = 0; 28 | 29 | void dispatchLogMsg(const char* logMsg); 30 | 31 | 32 | void sseq2midPutLog(Sseq2mid* sseq2mid, const char* logMessage); 33 | void sseq2midPutLogLine(Sseq2mid* sseq2mid, size_t offset, size_t size, 34 | const char* description, const char* comment); 35 | int sseq2midSseqChToMidiCh(Sseq2mid* sseq2mid, int sseqChannel); 36 | 37 | int getS1From(byte* data); 38 | int getS2LitFrom(byte* data); 39 | int getS3LitFrom(byte* data); 40 | int getS4LitFrom(byte* data); 41 | unsigned int getU1From(byte* data); 42 | unsigned int getU2LitFrom(byte* data); 43 | unsigned int getU3LitFrom(byte* data); 44 | unsigned int getU4LitFrom(byte* data); 45 | 46 | 47 | /* dispatch log message */ 48 | void dispatchLogMsg(const char* logMsg) 49 | { 50 | printf(logMsg); /* output to stdout */ 51 | } 52 | 53 | /* call the fuction to put log message */ 54 | void sseq2midPutLog(Sseq2mid* sseq2mid, const char* logMessage) 55 | { 56 | if(sseq2mid && sseq2mid->logProc) 57 | { 58 | sseq2mid->logProc(logMessage); 59 | } 60 | } 61 | 62 | #define SSEQ2MID_MAX_DUMP 5 63 | 64 | /* put log message in prescribed form */ 65 | void sseq2midPutLogLine(Sseq2mid* sseq2mid, size_t offset, size_t size, 66 | const char* description, const char* comment) 67 | { 68 | if(sseq2mid) 69 | { 70 | char logMsg[96]; 71 | char hexDump[24]; 72 | char hexDumpPart[8]; 73 | size_t sizeToTransfer; 74 | size_t transferedSize; 75 | byte* sseq = sseq2mid->sseq; 76 | size_t sseqSize = sseq2mid->sseqSize; 77 | 78 | sizeToTransfer = (offset + size <= sseqSize) ? size : sseqSize - offset; 79 | sizeToTransfer = (sizeToTransfer < SSEQ2MID_MAX_DUMP) ? sizeToTransfer : SSEQ2MID_MAX_DUMP; 80 | 81 | strcpy(hexDump, ""); 82 | for(transferedSize = 0; transferedSize < sizeToTransfer; transferedSize++) 83 | { 84 | if(transferedSize != 0) 85 | { 86 | strcat(hexDump, " "); 87 | } 88 | sprintf(hexDumpPart, "%02X", sseq[offset + transferedSize]); 89 | strcat(hexDump, hexDumpPart); 90 | } 91 | 92 | sprintf(logMsg, "%08X: %-14s | %-20s | %s\n", offset, hexDump, 93 | description ? description : "", comment ? comment : ""); 94 | sseq2midPutLog(sseq2mid, logMsg); 95 | } 96 | } 97 | 98 | /* filter: sseq channel number to midi track number */ 99 | int sseq2midSseqChToMidiCh(Sseq2mid* sseq2mid, int sseqChannel) 100 | { 101 | return ((sseqChannel >= 0) && (sseqChannel <= SSEQ_MAX_TRACK)) ? sseq2mid->chOrder[sseqChannel] : sseqChannel; 102 | } 103 | 104 | /* create sseq2mid object */ 105 | Sseq2mid* sseq2midCreate(const byte* sseq, size_t sseqSize, bool modifyChOrder) 106 | { 107 | Sseq2mid* newSseq2mid = (Sseq2mid*) calloc(1, sizeof(Sseq2mid)); 108 | 109 | if(newSseq2mid) 110 | { 111 | newSseq2mid->sseq = (byte*) malloc(sseqSize); 112 | if(newSseq2mid->sseq) 113 | { 114 | newSseq2mid->smf = smfCreate(); 115 | if(newSseq2mid->smf) 116 | { 117 | memcpy(newSseq2mid->sseq, sseq, sseqSize); 118 | newSseq2mid->sseqSize = sseqSize; 119 | 120 | smfSetTimebase(newSseq2mid->smf, 48); 121 | newSseq2mid->loopCount = 1; 122 | newSseq2mid->noReverb = false; 123 | newSseq2mid->modifyChOrder = modifyChOrder; 124 | } 125 | else 126 | { 127 | free(newSseq2mid->sseq); 128 | free(newSseq2mid); 129 | newSseq2mid = NULL; 130 | } 131 | } 132 | else 133 | { 134 | free(newSseq2mid); 135 | newSseq2mid = NULL; 136 | } 137 | } 138 | return newSseq2mid; 139 | } 140 | 141 | /* create sseq2mid object from file */ 142 | Sseq2mid* sseq2midCreateFromFile(const char* filename, bool modifyChOrder) 143 | { 144 | Sseq2mid* newSseq2mid = NULL; 145 | FILE* sseqFile = fopen(filename, "rb"); 146 | 147 | if(sseqFile) 148 | { 149 | size_t sseqSize; 150 | byte* sseq; 151 | 152 | fseek(sseqFile, 0, SEEK_END); 153 | sseqSize = (size_t) ftell(sseqFile); 154 | rewind(sseqFile); 155 | 156 | sseq = (byte*) malloc(sseqSize); 157 | if(sseq) 158 | { 159 | fread(sseq, sseqSize, 1, sseqFile); 160 | newSseq2mid = sseq2midCreate(sseq, sseqSize, modifyChOrder); 161 | free(sseq); 162 | } 163 | 164 | fclose(sseqFile); 165 | } 166 | return newSseq2mid; 167 | } 168 | 169 | /* delete sseq2mid object */ 170 | void sseq2midDelete(Sseq2mid* sseq2mid) 171 | { 172 | if(sseq2mid) 173 | { 174 | smfDelete(sseq2mid->smf); 175 | free(sseq2mid->sseq); 176 | free(sseq2mid); 177 | } 178 | } 179 | 180 | /* copy sseq2mid object */ 181 | Sseq2mid* sseq2midCopy(Sseq2mid* sseq2mid) 182 | { 183 | Sseq2mid* newSseq2mid = NULL; 184 | 185 | if(sseq2mid) 186 | { 187 | newSseq2mid = sseq2midCreate(sseq2mid->sseq, sseq2mid->sseqSize, sseq2mid->modifyChOrder); 188 | if(newSseq2mid) 189 | { 190 | smfDelete(newSseq2mid->smf); 191 | newSseq2mid->smf = smfCopy(sseq2mid->smf); 192 | if(newSseq2mid->smf) 193 | { 194 | sseq2midSetLogProc(newSseq2mid, sseq2mid->logProc); 195 | sseq2midSetLoopCount(newSseq2mid, sseq2mid->loopCount); 196 | } 197 | else 198 | { 199 | sseq2midDelete(newSseq2mid); 200 | newSseq2mid = NULL; 201 | } 202 | } 203 | } 204 | return newSseq2mid; 205 | } 206 | 207 | #define SSEQ_MIN_SIZE 0x1d 208 | 209 | /* sseq2mid conversion main, enjoy my dirty code :P */ 210 | bool sseq2midConvert(Sseq2mid* sseq2mid) 211 | { 212 | bool result = false; 213 | char strForLog[64]; 214 | int loopStartCount; 215 | size_t loopStartOffset; 216 | bool loopPointUsed = false; 217 | bool loopStartPointUsed = false; 218 | bool loopEndPointUsed = false; 219 | 220 | if(sseq2mid) 221 | { 222 | byte* sseq = sseq2mid->sseq; 223 | size_t sseqSize = sseq2mid->sseqSize; 224 | Smf* smf = sseq2mid->smf; 225 | 226 | if((sseqSize >= SSEQ_MIN_SIZE) && 227 | (sseq[0x00] == 'S') && (sseq[0x01] == 'S') && (sseq[0x02] == 'E') && (sseq[0x03] == 'Q') && 228 | (sseq[0x10] == 'D') && (sseq[0x11] == 'A') && (sseq[0x12] == 'T') && (sseq[0x13] == 'A')) 229 | { 230 | int trackIndex; 231 | int midiCh; 232 | size_t sseqOffsetBase; 233 | int midiChOrder[SSEQ_MAX_TRACK] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 9 }; 234 | 235 | /* put SSEQ header info */ 236 | sseq2midPutLogLine(sseq2mid, 0x00, 4, "Signature", "SSEQ"); 237 | sseq2midPutLogLine(sseq2mid, 0x04, 2, "", "Unknown"); 238 | sseq2midPutLogLine(sseq2mid, 0x06, 2, "", "Unknown"); 239 | sprintf(strForLog, "%u", getU4LitFrom(&sseq[0x08])); 240 | sseq2midPutLogLine(sseq2mid, 0x08, 4, "SSEQ file size", strForLog); 241 | sseq2midPutLogLine(sseq2mid, 0x0c, 2, "", "Unknown"); 242 | sseq2midPutLogLine(sseq2mid, 0x0e, 2, "", "Unknown"); 243 | sseq2midPutLog(sseq2mid, "\n"); 244 | 245 | /* put DATA chunk header */ 246 | sseq2midPutLogLine(sseq2mid, 0x10, 4, "Signature", "DATA"); 247 | sprintf(strForLog, "%u", getU4LitFrom(&sseq[0x14])); 248 | sseq2midPutLogLine(sseq2mid, 0x14, 4, "DATA chunk size", strForLog); 249 | sseqOffsetBase = (size_t) getU4LitFrom(&sseq[0x18]); 250 | sprintf(strForLog, "%08X", sseqOffsetBase); 251 | sseq2midPutLogLine(sseq2mid, 0x18, 4, "Offset Base", strForLog); 252 | sseq2midPutLog(sseq2mid, "\n"); 253 | 254 | /* initialize channel order */ 255 | for(midiCh = 0; midiCh < SSEQ_MAX_TRACK; midiCh++) 256 | { 257 | sseq2mid->chOrder[midiCh] = sseq2mid->modifyChOrder ? midiChOrder[midiCh] : midiCh; 258 | } 259 | 260 | /* initialize track settings */ 261 | sseq2mid->track[0].loopCount = sseq2mid->loopCount; 262 | sseq2mid->track[0].absTime = 0; 263 | sseq2mid->track[0].noteWait = false; 264 | sseq2mid->track[0].offsetToTop = 0x1c; 265 | sseq2mid->track[0].offsetToReturn = SSEQ_INVALID_OFFSET; 266 | sseq2mid->track[0].curOffset = sseq2mid->track[0].offsetToTop; 267 | for(trackIndex = 1; trackIndex < SSEQ_MAX_TRACK; trackIndex++) 268 | { 269 | sseq2mid->track[trackIndex].loopCount = 0; /* inactive */ 270 | sseq2mid->track[trackIndex].noteWait = false; 271 | } 272 | 273 | /* initialize midi */ 274 | #if 0 275 | smfInsertGM1SystemOn(smf, 0, 0, 0); 276 | #endif 277 | 278 | /* convert each track */ 279 | result = true; 280 | for(trackIndex = 0; trackIndex < SSEQ_MAX_TRACK; trackIndex++) 281 | { 282 | int loopCount = sseq2mid->track[trackIndex].loopCount; 283 | 284 | if(loopCount > 0) 285 | { 286 | do 287 | { 288 | int absTime = sseq2mid->track[trackIndex].absTime; 289 | size_t curOffset = sseq2mid->track[trackIndex].curOffset; 290 | size_t eventOffset = curOffset; 291 | char eventName[64]; 292 | char eventDesc[64]; 293 | bool eventException; 294 | size_t offsetToJump = SSEQ_INVALID_OFFSET; 295 | 296 | midiCh = sseq2midSseqChToMidiCh(sseq2mid, trackIndex); 297 | sprintf(eventName, "Access Violation"); 298 | sprintf(eventDesc, "End of File at %08X", sseqSize); 299 | eventException = true; 300 | 301 | if(curOffset < sseqSize) 302 | { 303 | byte statusByte; 304 | 305 | sseq2mid->track[trackIndex].offsetToAbsTime[curOffset] = absTime; 306 | 307 | statusByte = getU1From(&sseq[curOffset]); 308 | curOffset++; 309 | 310 | sprintf(eventName, "Unknown Event %02X", statusByte); 311 | sprintf(eventDesc, ""); 312 | eventException = false; 313 | 314 | if(statusByte < 0x80) 315 | { 316 | int velocity; 317 | int duration; 318 | const char* noteName[] = { 319 | "C ", "C#", "D ", "D#", "E ", "F ", 320 | "F#", "G ", "G#", "A ", "A#", "B " 321 | }; 322 | 323 | velocity = getU1From(&sseq[curOffset]); 324 | curOffset++; 325 | duration = smfReadVarLength(&sseq[curOffset], sseqSize - curOffset); 326 | curOffset += smfGetVarLengthSize(duration); 327 | 328 | smfInsertNote(smf, absTime, midiCh, midiCh, statusByte, velocity, duration); 329 | if(sseq2mid->track[trackIndex].noteWait) 330 | { 331 | absTime += duration; 332 | } 333 | 334 | sprintf(eventName, "Note with Duration"); 335 | sprintf(eventDesc, "%s %d [%d] vel:%-3d dur:%-3d", noteName[statusByte % 12], 336 | (statusByte / 12) - 1, statusByte, velocity, duration); 337 | } 338 | else 339 | { 340 | switch(statusByte) 341 | { 342 | case 0x80: 343 | { 344 | int tick; 345 | 346 | tick = smfReadVarLength(&sseq[curOffset], sseqSize - curOffset); 347 | curOffset += smfGetVarLengthSize(tick); 348 | 349 | absTime += tick; 350 | 351 | sprintf(eventName, "Rest"); 352 | sprintf(eventDesc, "%d", tick); 353 | break; 354 | } 355 | 356 | case 0x81: 357 | { 358 | int realProgram; 359 | int bankMsb; 360 | int bankLsb; 361 | int program; 362 | 363 | realProgram = smfReadVarLength(&sseq[curOffset], sseqSize - curOffset); 364 | curOffset += smfGetVarLengthSize(realProgram); 365 | 366 | program = realProgram % 128; 367 | bankLsb = (realProgram / 128) % 128; 368 | bankMsb = (realProgram / 128 / 128) % 128; 369 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_BANKSELM, bankMsb); 370 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_BANKSELL, bankLsb); 371 | smfInsertProgram(smf, absTime, midiCh, midiCh, program); 372 | 373 | sprintf(eventName, "Program Change"); 374 | sprintf(eventDesc, "%d", realProgram); 375 | break; 376 | } 377 | 378 | case 0x93: /* ('A`) */ 379 | { 380 | int newTrackIndex; 381 | int offset; 382 | 383 | newTrackIndex = getU1From(&sseq[curOffset]); 384 | curOffset += 1; 385 | offset = getU3LitFrom(&sseq[curOffset]) + sseqOffsetBase; 386 | curOffset += 3; 387 | 388 | sseq2mid->track[newTrackIndex].loopCount = loopCount; 389 | sseq2mid->track[newTrackIndex].absTime = absTime; 390 | sseq2mid->track[newTrackIndex].offsetToTop = offset; 391 | sseq2mid->track[newTrackIndex].offsetToReturn = SSEQ_INVALID_OFFSET; 392 | sseq2mid->track[newTrackIndex].curOffset = offset; 393 | 394 | sprintf(eventName, "Open Track"); 395 | sprintf(eventDesc, "Track %02d at %08Xh", newTrackIndex + 1, offset); 396 | break; 397 | } 398 | 399 | case 0x94: 400 | { 401 | int newOffset; 402 | 403 | newOffset = getU3LitFrom(&sseq[curOffset]) + sseqOffsetBase; 404 | curOffset += 3; 405 | 406 | offsetToJump = newOffset; 407 | 408 | if(offsetToJump >= sseq2mid->track[trackIndex].offsetToTop) 409 | { 410 | if(offsetToJump < curOffset) 411 | { 412 | switch(g_loopStyle) 413 | { 414 | case 0: 415 | loopCount--; 416 | break; 417 | 418 | case 1: 419 | if(!loopPointUsed) 420 | { 421 | smfInsertControl(smf, sseq2mid->track[trackIndex].offsetToAbsTime[offsetToJump], midiCh, midiCh, 0x74, 0); 422 | smfInsertControl(smf, absTime, midiCh, midiCh, 0x75, 0); 423 | loopPointUsed = true; 424 | } 425 | loopCount = 0; 426 | break; 427 | 428 | case 2: 429 | if(!loopPointUsed) 430 | { 431 | smfInsertMetaEvent(smf, sseq2mid->track[trackIndex].offsetToAbsTime[offsetToJump], midiCh, 6, "loopStart", 9); 432 | smfInsertMetaEvent(smf, absTime, midiCh, 6, "loopEnd", 7); 433 | loopPointUsed = true; 434 | } 435 | loopCount = 0; 436 | break; 437 | } 438 | } 439 | else 440 | { 441 | /* jump to forward */ 442 | } 443 | } 444 | else 445 | { 446 | /* redirect */ 447 | } 448 | 449 | sprintf(eventName, "Jump"); 450 | sprintf(eventDesc, "%08X", newOffset); 451 | break; 452 | } 453 | 454 | case 0x95: 455 | { 456 | int newOffset; 457 | 458 | newOffset = getU3LitFrom(&sseq[curOffset]) + sseqOffsetBase; 459 | curOffset += 3; 460 | 461 | sseq2mid->track[trackIndex].offsetToReturn = curOffset; 462 | offsetToJump = newOffset; 463 | 464 | sprintf(eventName, "Call"); 465 | sprintf(eventDesc, "%08X", newOffset); 466 | break; 467 | } 468 | 469 | case 0xa0: /* Hanjuku Hero DS: NSE_45, New Mario Bros: BGM_AMB_CHIKA, Slime Morimori Dragon Quest 2: SE_187, SE_210, Advance Wars */ 470 | { 471 | byte subStatusByte; 472 | int randMin; 473 | int randMax; 474 | 475 | subStatusByte = getU1From(&sseq[curOffset]); 476 | curOffset++; 477 | randMin = getU2LitFrom(&sseq[curOffset]); 478 | curOffset += 2; 479 | randMax = getU2LitFrom(&sseq[curOffset]); 480 | curOffset += 2; 481 | 482 | /* TODO: implement */ 483 | 484 | sprintf(eventName, "Random (%02X)", subStatusByte); 485 | sprintf(eventDesc, "Min:%d Max:%d", randMin, randMax); 486 | break; 487 | } 488 | 489 | case 0xa1: /* New Mario Bros: BGM_AMB_SABAKU */ 490 | { 491 | byte subStatusByte; 492 | int varNumber; 493 | 494 | subStatusByte = getU1From(&sseq[curOffset]); 495 | curOffset++; 496 | if(subStatusByte >= 0xb0 && subStatusByte <= 0xbd) /* var */ 497 | { 498 | /* loveemu is a lazy person :P */ 499 | curOffset++; 500 | varNumber = getU1From(&sseq[curOffset]); 501 | curOffset++; 502 | } 503 | else 504 | { 505 | varNumber = getU1From(&sseq[curOffset]); 506 | curOffset++; 507 | } 508 | 509 | /* TODO: implement */ 510 | 511 | sprintf(eventName, "From Var (%02X)", subStatusByte); 512 | sprintf(eventDesc, "var %d", varNumber); 513 | break; 514 | } 515 | 516 | case 0xa2: 517 | { 518 | /* TODO: implement */ 519 | 520 | sprintf(eventName, "If"); 521 | sprintf(eventDesc, ""); 522 | break; 523 | } 524 | 525 | case 0xb0: /* Children of Mana: SEQ_BGM001 */ 526 | case 0xb1: /* Advance Wars - Dual Strike: SE_TAGPT_COUNT01 */ 527 | case 0xb2: 528 | case 0xb3: 529 | case 0xb4: 530 | case 0xb5: 531 | case 0xb6: /* Mario Kart DS: 76th sequence */ 532 | case 0xb8: /* Tottoko Hamutaro: MUS_ENDROOL, Nintendogs */ 533 | case 0xb9: 534 | case 0xba: 535 | case 0xbb: 536 | case 0xbc: 537 | case 0xbd: 538 | { 539 | int varNumber; 540 | int var; 541 | const char* varMethodName[] = { 542 | "=", "+=", "-=", "*=", "/=", "[Shift]", "[Rand]", "", 543 | "==", ">=", ">", "<=", "<", "!=" 544 | }; 545 | 546 | varNumber = getU1From(&sseq[curOffset]); 547 | curOffset++; 548 | var = getU2LitFrom(&sseq[curOffset]); 549 | curOffset += 2; 550 | 551 | /* TODO: implement */ 552 | 553 | sprintf(eventName, "Variable %s", varMethodName[statusByte - 0xb0]); 554 | sprintf(eventDesc, "var %d : %d", varNumber, var); 555 | break; 556 | } 557 | 558 | case 0xc0: 559 | { 560 | int pan; 561 | 562 | pan = getU1From(&sseq[curOffset]); 563 | curOffset++; 564 | 565 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_PANPOT, pan); 566 | 567 | sprintf(eventName, "Pan"); 568 | sprintf(eventDesc, "%d", pan - 64); 569 | break; 570 | } 571 | 572 | case 0xc1: 573 | { 574 | int vol; 575 | 576 | vol = getU1From(&sseq[curOffset]); 577 | curOffset++; 578 | 579 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_VOLUME, vol); 580 | 581 | sprintf(eventName, "Volume"); 582 | sprintf(eventDesc, "%d", vol); 583 | break; 584 | } 585 | 586 | case 0xc2: /* Dawn of Sorrow: SDL_BGM_BOSS1_ */ 587 | { 588 | int vol; 589 | 590 | vol = getU1From(&sseq[curOffset]); 591 | curOffset++; 592 | 593 | smfInsertMasterVolume(smf, absTime, 0, midiCh, vol); 594 | 595 | sprintf(eventName, "Master Volume"); 596 | sprintf(eventDesc, "%d", vol); 597 | break; 598 | } 599 | 600 | case 0xc3: /* Puyo Pop Fever 2: BGM00 */ 601 | { 602 | int transpose; 603 | 604 | transpose = getS1From(&sseq[curOffset]); 605 | curOffset++; 606 | 607 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_RPNM, 0); 608 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_RPNL, 2); 609 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_DATAENTRYM, 64 + transpose); 610 | 611 | sprintf(eventName, "Transpose"); 612 | sprintf(eventDesc, "%d", transpose); 613 | break; 614 | } 615 | 616 | case 0xc4: 617 | { 618 | int bend; 619 | 620 | bend = getS1From(&sseq[curOffset]) * 64; 621 | curOffset++; 622 | 623 | smfInsertPitchBend(smf, absTime, midiCh, midiCh, bend); 624 | 625 | sprintf(eventName, "Pitch Bend"); 626 | sprintf(eventDesc, "%d", bend); 627 | break; 628 | } 629 | 630 | case 0xc5: 631 | { 632 | int range; 633 | 634 | range = getU1From(&sseq[curOffset]); 635 | curOffset++; 636 | 637 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_RPNM, 0); 638 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_RPNL, 0); 639 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_DATAENTRYM, range); 640 | 641 | sprintf(eventName, "Pitch Bend Range"); 642 | sprintf(eventDesc, "%d", range); 643 | break; 644 | } 645 | 646 | case 0xc6: /* Children of Mana: SEQ_BGM000 */ 647 | { 648 | int priority; 649 | 650 | priority = getU1From(&sseq[curOffset]); 651 | curOffset++; 652 | 653 | sprintf(eventName, "Priority"); 654 | sprintf(eventDesc, "%d", priority); 655 | break; 656 | } 657 | 658 | case 0xc7: /* Dawn of Sorrow: SDL_BGM_ARR1_ */ 659 | { 660 | int flg; 661 | 662 | flg = getU1From(&sseq[curOffset]); 663 | curOffset++; 664 | 665 | smfInsertControl(smf, absTime, midiCh, midiCh, flg ? SMF_CONTROL_MONO : SMF_CONTROL_POLY, 0); 666 | sseq2mid->track[trackIndex].noteWait = flg ? true : false; 667 | 668 | sprintf(eventName, "Mono/Poly"); 669 | sprintf(eventDesc, "%s (%d)", flg ? "Mono" : "Poly", flg); 670 | break; 671 | } 672 | 673 | case 0xc8: /* Hanjuku Hero DS: NSE_42 */ 674 | { 675 | int flg; 676 | 677 | flg = getU1From(&sseq[curOffset]); 678 | curOffset++; 679 | 680 | /* TODO: implement */ 681 | 682 | sprintf(eventName, "Tie"); 683 | sprintf(eventDesc, "%s (%d)", flg ? "On" : "Off", flg); 684 | break; 685 | } 686 | 687 | case 0xc9: /* Hanjuku Hero DS: NSE_50 */ 688 | { 689 | int key; 690 | 691 | key = getU1From(&sseq[curOffset]); 692 | curOffset++; 693 | 694 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_PORTAMENTOCTRL, key); 695 | 696 | sprintf(eventName, "Portamento Control"); 697 | sprintf(eventDesc, "%d", key); 698 | break; 699 | } 700 | 701 | case 0xca: /* Dawn of Sorrow: SDL_BGM_ARR1_ */ 702 | { 703 | int amount; 704 | 705 | amount = getU1From(&sseq[curOffset]); 706 | curOffset++; 707 | 708 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_MODULATION, amount); 709 | 710 | sprintf(eventName, "Modulation Depth"); 711 | sprintf(eventDesc, "%d", amount); 712 | break; 713 | } 714 | 715 | case 0xcb: /* Dawn of Sorrow: SDL_BGM_ARR1_ */ 716 | { 717 | int amount; 718 | 719 | amount = getU1From(&sseq[curOffset]); 720 | curOffset++; 721 | 722 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_VIBRATORATE, 64 + amount / 2); 723 | 724 | sprintf(eventName, "Modulation Speed"); 725 | sprintf(eventDesc, "%d", amount); 726 | break; 727 | } 728 | 729 | case 0xcc: /* Children of Mana: SEQ_BGM001 */ 730 | { 731 | int type; 732 | char* typeStr[] = { "Pitch", "Volume", "Pan" }; 733 | 734 | type = getU1From(&sseq[curOffset]); 735 | curOffset++; 736 | 737 | sprintf(eventName, "Modulation Type"); 738 | sprintf(eventDesc, "%s", typeStr[type]); 739 | break; 740 | } 741 | 742 | case 0xcd: /* Phoenix Wright: BGM021 */ 743 | { 744 | int amount; 745 | 746 | amount = getU1From(&sseq[curOffset]); 747 | curOffset++; 748 | 749 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_VIBRATODEPTH, 64 + amount / 2); 750 | 751 | sprintf(eventName, "Modulation Range"); 752 | sprintf(eventDesc, "%d", amount); 753 | break; 754 | } 755 | 756 | case 0xce: /* Dawn of Sorrow: SDL_BGM_ARR1_ */ 757 | { 758 | int flg; 759 | 760 | flg = getU1From(&sseq[curOffset]); 761 | curOffset++; 762 | 763 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_PORTAMENTO, !flg ? 0 : 127); 764 | 765 | sprintf(eventName, "Portamento"); 766 | sprintf(eventDesc, "%s (%d)", flg ? "On" : "Off", flg); 767 | break; 768 | } 769 | 770 | case 0xcf: /* Bomberman: SEQ_AREA04 */ 771 | { 772 | int time; 773 | 774 | time = getU1From(&sseq[curOffset]); 775 | curOffset++; 776 | 777 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_PORTAMENTOTIME, time); 778 | 779 | sprintf(eventName, "Portamento Time"); 780 | sprintf(eventDesc, "%d", time); 781 | break; 782 | } 783 | 784 | case 0xd0: /* Dawn of Sorrow: SDL_BGM_WIND_ */ 785 | { 786 | int amount; 787 | 788 | amount = getU1From(&sseq[curOffset]); 789 | curOffset++; 790 | #if 0 791 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_ATTACKTIME, 64 + amount / 2); 792 | #endif 793 | sprintf(eventName, "Attack Rate"); 794 | sprintf(eventDesc, "%d", amount); 795 | break; 796 | } 797 | 798 | case 0xd1: /* Dawn of Sorrow: SDL_BGM_WIND_ */ 799 | { 800 | int amount; 801 | 802 | amount = getU1From(&sseq[curOffset]); 803 | curOffset++; 804 | #if 0 805 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_DECAYTIME, 64 + amount / 2); 806 | #endif 807 | sprintf(eventName, "Decay Rate"); 808 | sprintf(eventDesc, "%d", amount); 809 | break; 810 | } 811 | 812 | case 0xd2: /* Dawn of Sorrow: SDL_BGM_WIND_ */ 813 | { 814 | int amount; 815 | 816 | amount = getU1From(&sseq[curOffset]); 817 | curOffset++; 818 | 819 | sprintf(eventName, "Sustain Rate"); 820 | sprintf(eventDesc, "%d", amount); 821 | break; 822 | } 823 | 824 | case 0xd3: /* Dawn of Sorrow: SDL_BGM_WIND_ */ 825 | { 826 | int amount; 827 | 828 | amount = getU1From(&sseq[curOffset]); 829 | curOffset++; 830 | #if 0 831 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_RELEASETIME, 64 + amount / 2); 832 | #endif 833 | sprintf(eventName, "Release Rate"); 834 | sprintf(eventDesc, "%d", amount); 835 | break; 836 | } 837 | 838 | case 0xd4: /* Dawn of Sorrow: SDL_BGM_WIND_ */ 839 | { 840 | 841 | loopStartCount = getU1From(&sseq[curOffset]); 842 | curOffset++; 843 | 844 | loopStartOffset = curOffset; 845 | if(loopStartCount == 0) 846 | { 847 | loopStartCount = -1; 848 | if(!loopStartPointUsed) 849 | { 850 | switch(g_loopStyle) 851 | { 852 | case 1: 853 | smfInsertControl(smf, absTime, midiCh, midiCh, 0x74, 0); 854 | break; 855 | case 2: 856 | smfInsertMetaEvent(smf, absTime, midiCh, 6, "loopStart", 9); 857 | break; 858 | } 859 | loopStartPointUsed = true; 860 | } 861 | } 862 | 863 | sprintf(eventName, "Loop Start"); 864 | sprintf(eventDesc, "%d", loopStartCount); 865 | break; 866 | } 867 | 868 | case 0xd5: 869 | { 870 | int expression; 871 | 872 | expression = getU1From(&sseq[curOffset]); 873 | curOffset++; 874 | 875 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_EXPRESSION, expression); 876 | 877 | sprintf(eventName, "Expression"); 878 | sprintf(eventDesc, "%d", expression); 879 | break; 880 | } 881 | 882 | case 0xd6: 883 | { 884 | int varNumber; 885 | 886 | varNumber = getU1From(&sseq[curOffset]); 887 | curOffset++; 888 | 889 | /* TODO: implement */ 890 | 891 | sprintf(eventName, "Print Variable"); 892 | sprintf(eventDesc, "%d", varNumber); 893 | break; 894 | } 895 | 896 | case 0xe0: /* Children of Mana: SEQ_BGM001 */ 897 | { 898 | int amount; 899 | 900 | amount = getU2LitFrom(&sseq[curOffset]); 901 | curOffset += 2; 902 | 903 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_VIBRATODELAY, 64 + amount / 2); 904 | 905 | sprintf(eventName, "Modulation Delay"); 906 | sprintf(eventDesc, "%d", amount); 907 | break; 908 | } 909 | 910 | case 0xe1: 911 | { 912 | int bpm; 913 | 914 | bpm = getU2LitFrom(&sseq[curOffset]); 915 | curOffset += 2; 916 | 917 | smfInsertTempoBPM(smf, absTime, midiCh, bpm); 918 | 919 | sprintf(eventName, "Tempo"); 920 | sprintf(eventDesc, "%d", bpm); 921 | break; 922 | } 923 | 924 | case 0xe3: /* Hippatte! Puzzle Bobble: SEQ_1pbgm03 */ 925 | { 926 | int amount; 927 | 928 | amount = getS2LitFrom(&sseq[curOffset]); 929 | curOffset += 2; 930 | 931 | smfInsertControl(smf, absTime, midiCh, midiCh, SMF_CONTROL_VIBRATODELAY, amount); 932 | 933 | sprintf(eventName, "Sweep Pitch"); 934 | sprintf(eventDesc, "%d", amount); 935 | break; 936 | } 937 | 938 | case 0xfc: /* Dawn of Sorrow: SDL_BGM_WIND_ */ 939 | { 940 | if(loopStartCount > 0) 941 | { 942 | loopStartCount--; 943 | curOffset = loopStartOffset; 944 | } 945 | if(loopStartCount == -1) 946 | { 947 | switch(g_loopStyle) 948 | { 949 | case 0: 950 | loopCount--; 951 | curOffset = loopStartOffset; 952 | break; 953 | case 1: 954 | if(!loopEndPointUsed) 955 | { 956 | smfInsertControl(smf, absTime, midiCh, midiCh, 0x75, 0); 957 | loopCount = 0; 958 | loopEndPointUsed = true; 959 | } 960 | break; 961 | case 2: 962 | if(!loopEndPointUsed) 963 | { 964 | smfInsertMetaEvent(smf, absTime, midiCh, 6, "loopEnd", 7); 965 | loopCount = 0; 966 | loopEndPointUsed = true; 967 | } 968 | break; 969 | } 970 | } 971 | 972 | sprintf(eventName, "Loop End"); 973 | sprintf(eventDesc, ""); 974 | break; 975 | } 976 | 977 | case 0xfd: 978 | { 979 | offsetToJump = sseq2mid->track[trackIndex].offsetToReturn; 980 | sseq2mid->track[trackIndex].offsetToReturn = SSEQ_INVALID_OFFSET; /* to avoid eternal loop */ 981 | 982 | if(offsetToJump == SSEQ_INVALID_OFFSET) 983 | { 984 | loopCount = 0; 985 | eventException = true; 986 | result = false; 987 | } 988 | 989 | sprintf(eventName, "Return"); 990 | sprintf(eventDesc, "%08X", offsetToJump); 991 | break; 992 | } 993 | 994 | case 0xfe: 995 | { 996 | int flag; 997 | unsigned int bit; 998 | 999 | flag = getU2LitFrom(&sseq[curOffset]); 1000 | curOffset += 2; 1001 | 1002 | if(sseq2mid->modifyChOrder) 1003 | { 1004 | int sseqCh; 1005 | int midiCh = 0; 1006 | 1007 | /* padding tracks, if necessary */ 1008 | bit = 1; 1009 | for(sseqCh = 0; sseqCh < SSEQ_MAX_TRACK; sseqCh++) 1010 | { 1011 | if(flag & bit) 1012 | { 1013 | sseq2mid->chOrder[sseqCh] = midiChOrder[midiCh]; 1014 | midiCh++; 1015 | } 1016 | bit = bit << 1; 1017 | } 1018 | bit = 1; 1019 | for(sseqCh = 0; sseqCh < SSEQ_MAX_TRACK; sseqCh++) 1020 | { 1021 | if(!(flag & bit)) 1022 | { 1023 | sseq2mid->chOrder[sseqCh] = midiChOrder[midiCh]; 1024 | midiCh++; 1025 | } 1026 | bit = bit << 1; 1027 | } 1028 | } 1029 | 1030 | sprintf(eventName, "Signify Multi Track"); 1031 | strcpy(eventDesc, ""); 1032 | for(bit = 1; bit < 0x10000; bit = bit << 1) 1033 | { 1034 | strcat(eventDesc, (flag & bit) ? "*" : "-"); 1035 | } 1036 | break; 1037 | } 1038 | 1039 | case 0xff: 1040 | { 1041 | loopCount = 0; 1042 | sprintf(eventName, "End of Track"); 1043 | sprintf(eventDesc, ""); 1044 | break; 1045 | } 1046 | 1047 | #if 0 1048 | case 0xfa: /* WarioWare Touched! */ 1049 | #endif 1050 | 1051 | default: 1052 | loopCount = 0; 1053 | eventException = true; 1054 | result = false; 1055 | break; 1056 | } 1057 | } 1058 | } 1059 | else 1060 | { 1061 | loopCount = 0; 1062 | } 1063 | 1064 | if(eventException) 1065 | { 1066 | fprintf(stderr, "warning: exception [%s - %s]\n", eventName, eventDesc); 1067 | strcat(eventDesc, " (!)"); 1068 | } 1069 | 1070 | sseq2midPutLogLine(sseq2mid, eventOffset, curOffset - eventOffset, eventName, eventDesc); 1071 | if(offsetToJump != SSEQ_INVALID_OFFSET) 1072 | { 1073 | curOffset = offsetToJump; 1074 | } 1075 | sseq2mid->track[trackIndex].absTime = absTime; 1076 | sseq2mid->track[trackIndex].curOffset = curOffset; 1077 | sseq2mid->track[trackIndex].loopCount = loopCount; 1078 | } while(loopCount > 0); 1079 | 1080 | if(sseq2mid->noReverb) 1081 | { 1082 | smfInsertControl(smf, 0, midiCh, midiCh, SMF_CONTROL_REVERB, 0); 1083 | } 1084 | smfSetEndTimingOfTrack(smf, midiCh, sseq2mid->track[midiCh].absTime); 1085 | sseq2midPutLog(sseq2mid, "\n"); 1086 | } 1087 | } 1088 | } 1089 | } 1090 | else 1091 | { 1092 | sseq2midPutLog(sseq2mid, "is not valid SSEQ\n"); 1093 | } 1094 | return result; 1095 | } 1096 | 1097 | /* output standard midi to memory from sseq2mid object */ 1098 | size_t sseq2midWriteMidi(Sseq2mid* sseq2mid, byte* buffer, size_t bufferSize) 1099 | { 1100 | return smfWrite(sseq2mid->smf, buffer, bufferSize); 1101 | } 1102 | 1103 | /* output standard midi file from sseq2mid object */ 1104 | size_t sseq2midWriteMidiFile(Sseq2mid* sseq2mid, const char* filename) 1105 | { 1106 | return smfWriteFile(sseq2mid->smf, filename); 1107 | } 1108 | 1109 | /* set log message procedure */ 1110 | void sseq2midSetLogProc(Sseq2mid* sseq2mid, Sseq2midLogProc* logProc) 1111 | { 1112 | if(sseq2mid && logProc) 1113 | { 1114 | sseq2mid->logProc = logProc; 1115 | } 1116 | } 1117 | 1118 | /* set reverb mode */ 1119 | bool sseq2midNoReverb(Sseq2mid* sseq2mid, bool noReverb) 1120 | { 1121 | bool oldNoReverb = false; 1122 | 1123 | if(sseq2mid) 1124 | { 1125 | oldNoReverb = sseq2mid->noReverb; 1126 | sseq2mid->noReverb = noReverb; 1127 | } 1128 | return oldNoReverb; 1129 | } 1130 | 1131 | /* set sequence loop count */ 1132 | int sseq2midSetLoopCount(Sseq2mid* sseq2mid, int loopCount) 1133 | { 1134 | int oldLoopCount = 1; 1135 | 1136 | if(sseq2mid) 1137 | { 1138 | oldLoopCount = sseq2mid->loopCount; 1139 | if(loopCount >= 0) 1140 | { 1141 | sseq2mid->loopCount = (loopCount != 0) ? loopCount : 1; 1142 | } 1143 | } 1144 | return oldLoopCount; 1145 | } 1146 | 1147 | 1148 | /* get signed byte */ 1149 | int getS1From(byte* data) 1150 | { 1151 | int val = data[0]; 1152 | return (val & 0x80) ? -(signed) (0xFF-val+1) : val; 1153 | } 1154 | 1155 | /* get signed 2 bytes as little endian */ 1156 | int getS2LitFrom(byte* data) 1157 | { 1158 | int val = data[0] | (data[1] << 8); 1159 | return (val & 0x8000) ? -(signed) (0xFFFF-val+1) : val; 1160 | } 1161 | 1162 | /* get signed 3 bytes as little endian */ 1163 | int getS3LitFrom(byte* data) 1164 | { 1165 | int val = data[0] | (data[1] << 8) | (data[2] << 16); 1166 | return (val & 0x800000) ? -(signed) (0xFFFFFF-val+1) : val; 1167 | } 1168 | 1169 | /* get signed 4 bytes as little endian */ 1170 | int getS4LitFrom(byte* data) 1171 | { 1172 | int val = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); 1173 | return (val & 0x80000000) ? -(signed) (0xFFFFFFFF-val+1) : val; 1174 | } 1175 | 1176 | /* get unsigned byte */ 1177 | unsigned int getU1From(byte* data) 1178 | { 1179 | return (unsigned int) data[0]; 1180 | } 1181 | 1182 | /* get unsigned 2 bytes as little endian */ 1183 | unsigned int getU2LitFrom(byte* data) 1184 | { 1185 | return (unsigned int) (data[0] | (data[1] << 8)); 1186 | } 1187 | 1188 | /* get unsigned 3 bytes as little endian */ 1189 | unsigned int getU3LitFrom(byte* data) 1190 | { 1191 | return (unsigned int) (data[0] | (data[1] << 8) | (data[2] << 16)); 1192 | } 1193 | 1194 | /* get unsigned 4 bytes as little endian */ 1195 | unsigned int getU4LitFrom(byte* data) 1196 | { 1197 | return (unsigned int) (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); 1198 | } 1199 | -------------------------------------------------------------------------------- /src/decoder/sseq2mid.h: -------------------------------------------------------------------------------- 1 | /** 2 | * sseq2mid.h: convert sseq into standard midi 3 | * presented by loveemu, feel free to redistribute 4 | */ 5 | 6 | #ifndef SSEQ2MID_H 7 | #define SSEQ2MID_H 8 | 9 | 10 | #include 11 | #include "libsmfc.h" 12 | #include "libsmfcx.h" 13 | 14 | 15 | #define SSEQ_INVALID_OFFSET -1 16 | 17 | typedef struct TagSseq2midTrackState 18 | { 19 | int loopCount; 20 | int absTime; 21 | bool noteWait; 22 | size_t curOffset; 23 | size_t offsetToTop; 24 | size_t offsetToReturn; 25 | int offsetToAbsTime[ 262144 ]; // XXX 26 | } Sseq2midTrackState; 27 | 28 | 29 | #define SSEQ_MAX_TRACK 16 30 | 31 | typedef void (Sseq2midLogProc)(const char*); 32 | 33 | typedef struct TagSseq2mid 34 | { 35 | byte* sseq; 36 | size_t sseqSize; 37 | Smf* smf; 38 | Sseq2midTrackState track[SSEQ_MAX_TRACK]; 39 | Sseq2midLogProc* logProc; 40 | int chOrder[SSEQ_MAX_TRACK]; 41 | bool modifyChOrder; 42 | bool noReverb; 43 | int loopCount; 44 | } Sseq2mid; 45 | 46 | Sseq2mid* sseq2midCreate(const byte* sseq, size_t sseqSize, bool modifyChOrder); 47 | Sseq2mid* sseq2midCreateFromFile(const char* filename, bool modifyChOrder); 48 | void sseq2midDelete(Sseq2mid* sseq2mid); 49 | Sseq2mid* sseq2midCopy(Sseq2mid* sseq2mid); 50 | bool sseq2midConvert(Sseq2mid* sseq2mid); 51 | size_t sseq2midWriteMidi(Sseq2mid* sseq2mid, byte* buffer, size_t bufferSize); 52 | size_t sseq2midWriteMidiFile(Sseq2mid* sseq2mid, const char* filename); 53 | void sseq2midSetLogProc(Sseq2mid* sseq2mid, Sseq2midLogProc* logProc); 54 | bool sseq2midNoReverb(Sseq2mid* sseq2mid, bool noReverb); 55 | int sseq2midSetLoopCount(Sseq2mid* sseq2mid, int loopCount); 56 | 57 | 58 | #endif /* !SSEQ2MID_H */ 59 | -------------------------------------------------------------------------------- /src/htools/macrotools.h: -------------------------------------------------------------------------------- 1 | #define STRINGIFY(X) STRINGIFY2(X) 2 | #define STRINGIFY2(X) #X 3 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "main.h" 7 | #include "sdatxtract.hpp" 8 | 9 | //====== global vars ======= 10 | bool bDecodeFile = false; 11 | 12 | bool bVerboseMessages = false; 13 | 14 | bool bExtractSdat = false; 15 | 16 | bool bUseFname = true; 17 | 18 | bool bGetSwav = false; 19 | 20 | const struct option long_options[] = { 21 | { "output", required_argument, NULL, 'o', }, 22 | { "convert", no_argument, NULL, 'c', }, 23 | { "extract-swar", no_argument, NULL, 'x', }, 24 | { "force-numeric", no_argument, NULL, 'n', }, 25 | { "extract-sdat", no_argument, NULL, 'S', }, 26 | { "verbose", no_argument, NULL, 'V', }, 27 | { "help", no_argument, NULL, 'h', }, 28 | { NULL, no_argument, NULL, 0, }, 29 | }; 30 | 31 | void printUsage(void) { 32 | const char *options[] = { 33 | "", "-c", "output converted files", 34 | "", "-x", "extract swav from swar", 35 | "", "-n", "force output with numericaly assigned filenames", 36 | "", "-S", "extract sdat only", 37 | "", "-V", "show verbose messages", 38 | "", "--help or -h", "show this usage", 39 | }; 40 | printf("%s v%s\n", APP_NAME, APP_VERSION_FULL); 41 | printf("Sdat sound archive extraction utility for nds games.\n"); 42 | printf("%s is able to read most sdat files to extract (some) of \nthe audio data they hold.\n\n", APP_NAME); 43 | printf("Usage: sdatxtract \n"); 44 | printf("Options:\n"); 45 | for(long unsigned optIndex = 0; optIndex < sizeof(options) / sizeof(options[optIndex]); optIndex += 3){ 46 | printf("%-2s %-12s %s\n", options[optIndex], options[optIndex + 1], options[optIndex + 2]); 47 | } 48 | } 49 | 50 | 51 | /* dispatch option char */ 52 | bool GET_OptionChar(int opt) 53 | { 54 | switch(opt) 55 | { 56 | case 'c': 57 | bDecodeFile = true; 58 | return true; 59 | case 'x': 60 | bGetSwav = true; 61 | return true; 62 | case 'n': 63 | bUseFname = false; 64 | return true; 65 | case 'h': 66 | printUsage(); 67 | return true; 68 | case 'S': 69 | bExtractSdat = true; 70 | return true; 71 | case 'V': 72 | bVerboseMessages = true; 73 | return true; 74 | 75 | case ':': 76 | printf("option needs a value\n"); 77 | return false; 78 | case '?': //used for some unknown options 79 | printf("unknown option: %c\n", optopt); 80 | return true; 81 | } 82 | return false; 83 | } 84 | 85 | int main(int argc, char* argv[]) 86 | { 87 | if(argc <= 1){ 88 | /* no arguments */ 89 | printUsage(); 90 | } else { 91 | int opt; 92 | while((opt = getopt_long(argc, argv, ":cxnhSV", long_options, NULL)) != -1) 93 | if (!GET_OptionChar(opt)) 94 | return 1; 95 | } 96 | 97 | verbose("======================\n"); 98 | verbose("Options: (1=enabled/0=disabled)\n"); 99 | verbose("bDecodeFile:%d\n", bDecodeFile); 100 | verbose("bGetSwav:%d\n", bGetSwav); 101 | verbose("bVerboseMessages:%d\n", bVerboseMessages); 102 | verbose("bUseFname:%d\n", bUseFname); 103 | verbose("bExtractSdat:%d\n", bExtractSdat); 104 | verbose("======================\n"); 105 | 106 | /* input files */ 107 | for(; optind < argc; optind++) { 108 | bool isNds = false; 109 | 110 | std::vector sdats = SdatX::Init(argv[optind], isNds); 111 | if (!sdats.size()) { 112 | warning("No SDATs found in file: %s\n", argv[optind]); 113 | } else { 114 | if (isNds) { 115 | std::string outdir = fs::path(argv[optind]).stem().string(); 116 | fs::create_directory(outdir); 117 | fs::current_path(outdir); 118 | } 119 | 120 | for (uint32_t i = 0; i < sdats.size(); i++) { 121 | if (bExtractSdat) { 122 | if (!sdats[i].Write()) 123 | warning("Failed to write SDAT: %s\n", argv[optind]); 124 | } else { 125 | if (!sdats[i].Extract()) 126 | warning("Failed to extract file: %s\n", argv[optind]); 127 | } 128 | } 129 | 130 | if (isNds) 131 | fs::current_path(".."); 132 | } 133 | 134 | sdats.clear(); 135 | } 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #ifndef __SDATXTRACT_MAIN_H__ 2 | #define __SDATXTRACT_MAIN_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "config.h" //Compile settings. 13 | #include "common.hpp" 14 | 15 | #include "htools/macrotools.h" 16 | 17 | #define MAX_PATH 260 18 | 19 | #define DIR_SSEQ "sequence" 20 | #define DIR_SWAR "wave" 21 | #define DIR_SBNK "bank" 22 | #define DIR_STRM "stream" 23 | 24 | // Verbose messages. 25 | #define verbose(...) if(bVerboseMessages){printf(__VA_ARGS__);} 26 | #define processIndicator() printf("."); 27 | #define DEBUG(...) printf("[" __FILE__ "] " STRINGIFY(__LINE__) ": " __VA_ARGS__) 28 | 29 | #define error(...) printf("[Err]: " __VA_ARGS__) 30 | #define warning(...) printf("[Warn]: " __VA_ARGS__) 31 | 32 | namespace File { 33 | static inline bool Exists(const std::string &filename) { 34 | FILE *fp = fopen(filename.c_str(), "rb"); 35 | 36 | if(!fp) { 37 | return false; 38 | } 39 | 40 | fclose(fp); 41 | 42 | return true; 43 | } 44 | } 45 | 46 | namespace fs = std::filesystem; 47 | 48 | // Global variables. 49 | extern bool bDecodeFile; 50 | extern bool bVerboseMessages; 51 | extern bool bExtractSdat; 52 | extern bool bUseFname; 53 | extern bool bGetSwav; 54 | #endif 55 | -------------------------------------------------------------------------------- /src/memfile.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __SDATXTRACT_MEMFILE_H__ 2 | #define __SDATXTRACT_MEMFILE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class MemFile { 9 | public: 10 | MemFile(const std::string& filename, uint32_t length) : m_FileName(filename), m_Length(length) { 11 | m_Data = new uint8_t[length]; 12 | m_IsInit = true; 13 | } 14 | 15 | MemFile(const std::string& filename, uint8_t* ptr, uint32_t length) : m_FileName(filename), m_Length(length) { 16 | m_Data = new uint8_t[m_Length]; 17 | memcpy(m_Data, ptr, m_Length); 18 | m_IsInit = true; 19 | } 20 | 21 | ~MemFile() { 22 | if (m_Data) { 23 | delete[] m_Data; 24 | m_Data = NULL; 25 | } 26 | } 27 | 28 | std::string GetFilename() { 29 | return m_FileName; 30 | } 31 | 32 | uint32_t GetSize() { 33 | return m_Length; 34 | } 35 | 36 | uint8_t* GetRawPtr() { 37 | return m_Data; 38 | } 39 | 40 | uint8_t* GetCurPos() { 41 | return m_Data+m_Offset; 42 | } 43 | 44 | bool Good() { 45 | return m_IsInit; 46 | } 47 | 48 | long unsigned GetOffset() { 49 | return m_Offset; 50 | } 51 | 52 | template 53 | T Read(bool littleEndian = true) { 54 | if (m_Offset+sizeof(T) > m_Length) 55 | return 0; 56 | 57 | T a; 58 | memcpy(&a, m_Data + m_Offset, sizeof(T)); 59 | m_Offset+=sizeof(T); 60 | return a; 61 | } 62 | 63 | int32_t ReadFixLen(size_t bytes, bool littleEndian = true, bool isSigned = false) 64 | { 65 | int32_t result = 0; 66 | 67 | for (size_t i = 0; i < bytes; ++i) 68 | { 69 | result |= *(m_Data+(m_Offset++)) << ((littleEndian ? i : bytes - i - 1) * 8); 70 | } 71 | 72 | if (isSigned && (result >= (1 << ((bytes * 8) - 1)))) 73 | { 74 | result -= 1 << (bytes * 8); 75 | } 76 | 77 | return result; 78 | } 79 | 80 | int32_t ReadVarLen() 81 | { 82 | int32_t result = 0; 83 | 84 | do 85 | { 86 | result = (result << 7) | (*GetCurPos() & 0x7F); 87 | } while (*(m_Data+(m_Offset++)) & 0x80); 88 | 89 | return result; 90 | } 91 | 92 | template 93 | size_t ReadArray(T* in_buff, size_t size) { 94 | if (m_Offset+size > m_Length) 95 | return 0; 96 | 97 | memcpy(static_cast(in_buff), (void*)(m_Data + m_Offset), size); 98 | m_Offset+=size; 99 | return size; 100 | } 101 | 102 | long unsigned m_Offset = 0; 103 | 104 | private: 105 | std::string m_FileName = "filename not avaliable"; 106 | uint8_t* m_Data = NULL; 107 | uint32_t m_Length = 0; 108 | bool m_IsInit; 109 | }; 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/sdat.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include "sdat.hpp" 3 | #include "common.hpp" 4 | 5 | bool SDAT::Verify(const std::string& filename) { 6 | FILE *fp = fopen(filename.c_str(), "rb"); 7 | uint8_t sdatMagic[] = {'S','D','A','T'}; 8 | char magic[sizeof(sdatMagic)+1]; 9 | 10 | if(!fp) { 11 | return false; 12 | } 13 | 14 | fread(magic, 1, sizeof(sdatMagic), fp); 15 | 16 | fclose(fp); 17 | 18 | if(!memcmp(magic, sdatMagic, sizeof(sdatMagic))) 19 | return true; 20 | 21 | return false; 22 | } 23 | 24 | SdatSymb SDATi_ProcessRecordSYMB(MemFile& mf, uint32_t symbOffset, uint32_t recOffset) { 25 | mf.m_Offset = symbOffset+recOffset; 26 | 27 | uint32_t nEntries = mf.Read(); 28 | SdatSymb records(nEntries); 29 | 30 | verbose("Records: %ld\n", records.size()); 31 | 32 | for (uint32_t i = 0; i < records.size(); i++) { 33 | uint32_t offset = mf.Read(); 34 | 35 | if (offset != 0) 36 | { 37 | records[i].Offset = (mf.GetRawPtr()+symbOffset)+offset; 38 | records[i].Name = std::string(reinterpret_cast(records[i].Offset)); 39 | 40 | verbose("SYMB entry #%d: %s\n", i, records[i].Name.c_str()); 41 | } else { 42 | records[i].Offset = 0; 43 | records[i].Name = ""; 44 | } 45 | } 46 | 47 | return records; 48 | } 49 | 50 | template 51 | std::vector SDATi_ProcessRecordINFO(MemFile& mf, uint32_t infoOffset, uint32_t recOffset) { 52 | mf.m_Offset = infoOffset+recOffset; 53 | 54 | uint32_t nEntries = mf.Read(); 55 | std::vector nEntryOffsets(nEntries); 56 | 57 | for (uint32_t i = 0; i < nEntryOffsets.size(); i++) 58 | nEntryOffsets[i] = mf.Read(); 59 | 60 | std::vector records(nEntries); 61 | for (uint32_t i = 0; i < records.size(); i++) { 62 | if (nEntryOffsets[i]) 63 | records[i] = T(mf, infoOffset+nEntryOffsets[i]); 64 | //verbose("INFO entry ID #%d: %d\n", i, records[i].fileId); 65 | } 66 | 67 | return records; 68 | } 69 | 70 | bool SDAT::ReadHeader(MemFile& mf, SdatFiles& files) { 71 | // SDAT header 72 | if (!Common::Assert(mf, 0x53444154, mf.ReadFixLen(4, false))) { return false; } 73 | if (!Common::Assert(mf, 0xFEFF, mf.Read())) { return false; } 74 | mf.Read(); // Version 75 | if (!Common::Assert(mf, mf.GetSize(), mf.Read())) { return false; } 76 | if (!Common::Assert(mf, 0x40, mf.Read())) { return false; } 77 | uint16_t numBlocks = mf.Read(); 78 | 79 | // Read block offsets 80 | uint32_t symbOffset = mf.Read(); 81 | uint32_t symbLength = mf.Read(); 82 | 83 | uint32_t infoOffset = mf.Read(); 84 | uint32_t infoLength = mf.Read(); 85 | 86 | uint32_t fatOffset = mf.Read(); 87 | uint32_t fatLength = mf.Read(); 88 | 89 | uint32_t fileOffset = mf.Read(); 90 | uint32_t fileLength = mf.Read(); 91 | 92 | std::vector symbs; 93 | bool haveSYMBs = symbOffset && symbLength; 94 | if (haveSYMBs) { 95 | std::vector nRecOffset(8); 96 | 97 | mf.m_Offset = symbOffset; 98 | 99 | // SYMB block 100 | if (!Common::Assert(mf, 0x53594D42, mf.ReadFixLen(4, false))) { return false; } 101 | if (!Common::Assert(mf, symbLength, mf.Read())); // Continue execution 102 | for (uint32_t i = 0; i < nRecOffset.size(); i++) 103 | nRecOffset[i] = mf.Read(); 104 | mf.m_Offset+=24; 105 | 106 | verbose("Reading SSEQ symbols.\n"); 107 | symbs.push_back(SDATi_ProcessRecordSYMB(mf, symbOffset, nRecOffset[SDATR_SSEQ])); 108 | verbose("Reading SBNK symbols.\n"); 109 | symbs.push_back(SDATi_ProcessRecordSYMB(mf, symbOffset, nRecOffset[SDATR_SBNK])); 110 | verbose("Reading SWAR symbols.\n"); 111 | symbs.push_back(SDATi_ProcessRecordSYMB(mf, symbOffset, nRecOffset[SDATR_SWAR])); 112 | verbose("Reading STRM symbols.\n"); 113 | symbs.push_back(SDATi_ProcessRecordSYMB(mf, symbOffset, nRecOffset[SDATR_STRM])); 114 | } else { 115 | warning("SYMB block not aviable, filenames will be set automatically.\n"); 116 | } 117 | 118 | SdatInfo fInfo; 119 | if (infoOffset && infoLength) { 120 | std::vector nRecOffset(8); 121 | 122 | mf.m_Offset = infoOffset; 123 | 124 | // INFO block 125 | if (!Common::Assert(mf, 0x494E464F, mf.ReadFixLen(4, false))) { return false; } 126 | if (!Common::Assert(mf, infoLength, mf.Read())) { return false; } 127 | for (uint32_t i = 0; i < nRecOffset.size(); i++) 128 | nRecOffset[i] = mf.Read(); 129 | mf.m_Offset+=24; 130 | 131 | verbose("Reading SSEQ info.\n"); 132 | fInfo.sseqinfo = SDATi_ProcessRecordINFO(mf, infoOffset, nRecOffset[SDATR_SSEQ]); 133 | verbose("Reading SBNK info.\n"); 134 | fInfo.sbnkinfo = SDATi_ProcessRecordINFO(mf, infoOffset, nRecOffset[SDATR_SBNK]); 135 | verbose("Reading SWAR info.\n"); 136 | fInfo.swarinfo = SDATi_ProcessRecordINFO(mf, infoOffset, nRecOffset[SDATR_SWAR]); 137 | verbose("Reading STRM info.\n"); 138 | fInfo.strminfo = SDATi_ProcessRecordINFO(mf, infoOffset, nRecOffset[SDATR_STRM]); 139 | } else { 140 | error("No INFO block found. Cannot continue\n"); 141 | return false; 142 | } 143 | 144 | std::vector fatEnts; 145 | if (fatOffset && fatLength) { 146 | mf.m_Offset = fatOffset; 147 | 148 | // FAT block 149 | if (!Common::Assert(mf, 0x46415420, mf.ReadFixLen(4, false))) { return false; } 150 | if (!Common::Assert(mf, fatLength, mf.Read())) { return false; } 151 | 152 | verbose("Reading files.\n"); 153 | uint32_t nEntries = mf.Read(); 154 | for (uint32_t i = 0; i < nEntries; i++) { 155 | FTableEntry entry; 156 | uint32_t offset = mf.Read(); 157 | 158 | entry.Offset = mf.GetRawPtr()+offset; 159 | entry.Length = mf.Read(); 160 | 161 | fatEnts.push_back(entry); 162 | mf.m_Offset+=8; 163 | } 164 | } else { 165 | error("No FAT block found. Cannot continue\n"); 166 | return false; 167 | } 168 | 169 | for (uint32_t i = 0; i < fInfo.sseqinfo.size(); i++) { 170 | uint16_t id = fInfo.sseqinfo[i].fileId; 171 | 172 | if (id == (uint16_t)-1) 173 | continue; 174 | 175 | SdatSseq sseq; 176 | FTableEntry entry = fatEnts[id]; 177 | sseq.Offset = entry.Offset; 178 | sseq.Length = entry.Length; 179 | if (!bUseFname || !haveSYMBs || symbs[SDATI_SSEQ][i].Name.empty()) 180 | sseq.FileName = "SSEQ_"+std::to_string(i); 181 | else 182 | sseq.FileName = symbs[SDATI_SSEQ][i].Name; 183 | 184 | files.sseqs.push_back(sseq); 185 | } 186 | 187 | for (uint32_t i = 0; i < fInfo.sbnkinfo.size(); i++) { 188 | uint16_t id = fInfo.sbnkinfo[i].fileId; 189 | 190 | if (id == (uint16_t)-1) 191 | continue; 192 | 193 | SdatSbnk sbnk; 194 | FTableEntry entry = fatEnts[id]; 195 | sbnk.Offset = entry.Offset; 196 | sbnk.Length = entry.Length; 197 | if (!bUseFname || !haveSYMBs || symbs[SDATI_SBNK][i].Name.empty()) 198 | sbnk.FileName = "SBNK_"+std::to_string(i); 199 | else 200 | sbnk.FileName = symbs[SDATI_SBNK][i].Name; 201 | 202 | files.sbnks.push_back(sbnk); 203 | } 204 | 205 | for (uint32_t i = 0; i < fInfo.swarinfo.size(); i++) { 206 | uint16_t id = fInfo.swarinfo[i].fileId; 207 | 208 | if (id == (uint16_t)-1) 209 | continue; 210 | 211 | SdatSwar swar; 212 | FTableEntry entry = fatEnts[id]; 213 | swar.Offset = entry.Offset; 214 | swar.Length = entry.Length; 215 | if (!bUseFname || !haveSYMBs || symbs[SDATI_SWAR][i].Name.empty()) 216 | swar.FileName = "SWAR_"+std::to_string(i); 217 | else 218 | swar.FileName = symbs[SDATI_SWAR][i].Name; 219 | 220 | files.swars.push_back(swar); 221 | } 222 | 223 | for (uint32_t i = 0; i < fInfo.strminfo.size(); i++) { 224 | uint16_t id = fInfo.strminfo[i].fileId; 225 | 226 | if (id == (uint16_t)-1) 227 | continue; 228 | 229 | SdatStrm strm; 230 | FTableEntry entry = fatEnts[id]; 231 | strm.Offset = entry.Offset; 232 | strm.Length = entry.Length; 233 | if (!bUseFname || !haveSYMBs || symbs[SDATI_STRM][i].Name.empty()) 234 | strm.FileName = "STRM_"+std::to_string(i); 235 | else 236 | strm.FileName = symbs[SDATI_STRM][i].Name; 237 | 238 | files.strms.push_back(strm); 239 | } 240 | 241 | return true; 242 | } 243 | -------------------------------------------------------------------------------- /src/sdat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "memfile.hpp" 4 | 5 | enum sdatrecord { 6 | SDATR_SSEQ = 0, // Sequence 7 | SDATR_SSEA, // Sequence Archive 8 | SDATR_SBNK, // Bank 9 | SDATR_SWAR, // Wave Archive 10 | SDATR_SPLR, // Player 11 | SDATR_SGRP, // Group 12 | SDATR_SSPL, // Sound Player 13 | SDATR_STRM, // Stream 14 | }; 15 | 16 | enum sdatIlist { 17 | SDATI_SSEQ = 0, // Sequence 18 | SDATI_SBNK, // Bank 19 | SDATI_SWAR, // Wave Archive 20 | SDATI_STRM, // Stream 21 | }; 22 | 23 | struct SSEQInfo { 24 | uint16_t fileId; 25 | uint16_t unk0; 26 | uint16_t bank; // Associated BANK 27 | uint8_t volume; // Volume 28 | uint8_t chnPrio; // Channel Priority 29 | uint8_t plrPrio; // Player Priority 30 | uint8_t players; 31 | uint8_t unk1[2]; 32 | 33 | SSEQInfo(MemFile& mf, uint32_t baseOffset) { 34 | mf.m_Offset = baseOffset; 35 | fileId = mf.Read(); 36 | unk0 = mf.Read(); 37 | bank = mf.Read(); 38 | volume = mf.Read(); 39 | chnPrio = mf.Read(); 40 | plrPrio = mf.Read(); 41 | players = mf.Read(); 42 | mf.ReadArray(unk1, sizeof(unk1)); 43 | } 44 | 45 | SSEQInfo() { 46 | fileId = (uint16_t)-1; 47 | unk0 = 0; 48 | bank = 0; 49 | volume = 0; 50 | chnPrio = 0; 51 | plrPrio = 0; 52 | players = 0; 53 | } 54 | 55 | ~SSEQInfo() {} 56 | }; 57 | 58 | struct SBNKInfo { 59 | uint16_t fileId; 60 | uint16_t unk0; 61 | uint16_t wars[4]; // Associated WAVEARC. 0xffff if not in use 62 | 63 | SBNKInfo(MemFile& mf, uint32_t baseOffset) { 64 | mf.m_Offset = baseOffset; 65 | fileId = mf.Read(); 66 | unk0 = mf.Read(); 67 | mf.ReadArray(wars, sizeof(wars)); 68 | } 69 | 70 | SBNKInfo() { 71 | fileId = (uint16_t)-1; 72 | unk0 = 0; 73 | } 74 | 75 | ~SBNKInfo() {} 76 | }; 77 | 78 | struct SWARInfo { 79 | uint16_t fileId; 80 | uint16_t unk0; 81 | 82 | SWARInfo(MemFile& mf, uint32_t baseOffset) { 83 | mf.m_Offset = baseOffset; 84 | fileId = mf.Read(); 85 | unk0 = mf.Read(); 86 | } 87 | 88 | SWARInfo() { 89 | fileId = (uint16_t)-1; 90 | unk0 = 0; 91 | } 92 | 93 | ~SWARInfo() {} 94 | }; 95 | 96 | struct STRMInfo { 97 | uint16_t fileId; 98 | uint16_t unk0; 99 | uint8_t volume; 100 | uint8_t prio; // Priority 101 | uint8_t players; 102 | uint8_t reserved[5]; 103 | 104 | STRMInfo(MemFile& mf, uint32_t baseOffset) { 105 | mf.m_Offset = baseOffset; 106 | fileId = mf.Read(); 107 | unk0 = mf.Read(); 108 | volume = mf.Read(); 109 | prio = mf.Read(); 110 | players = mf.Read(); 111 | mf.ReadArray(reserved, sizeof(reserved)); 112 | } 113 | 114 | STRMInfo() { 115 | fileId = (uint16_t)-1; 116 | unk0 = 0; 117 | volume = 0; 118 | prio = 0; 119 | players = 0; 120 | } 121 | 122 | ~STRMInfo() {} 123 | }; 124 | 125 | struct SdatInfo 126 | { 127 | std::vector sseqinfo; 128 | std::vector sbnkinfo; 129 | std::vector swarinfo; 130 | std::vector strminfo; 131 | }; 132 | 133 | struct FTableEntry 134 | { 135 | uint8_t* Offset; 136 | uint32_t Length; 137 | }; 138 | 139 | struct SymbRecord 140 | { 141 | uint8_t* Offset; 142 | 143 | std::string Name; 144 | }; 145 | 146 | typedef std::vector SdatSymb; 147 | 148 | struct SdatSseq 149 | { 150 | uint8_t* Offset; 151 | uint32_t Length; 152 | 153 | std::string FileName; 154 | }; 155 | 156 | struct SdatSbnk 157 | { 158 | uint8_t* Offset; 159 | uint32_t Length; 160 | 161 | std::string FileName; 162 | }; 163 | 164 | struct SdatSwar 165 | { 166 | uint8_t* Offset; 167 | uint32_t Length; 168 | 169 | std::string FileName; 170 | }; 171 | 172 | struct SdatStrm 173 | { 174 | uint8_t* Offset; 175 | uint32_t Length; 176 | 177 | std::string FileName; 178 | }; 179 | 180 | struct SdatFiles 181 | { 182 | std::vector sseqs; 183 | std::vector sbnks; 184 | std::vector swars; 185 | std::vector strms; 186 | }; 187 | 188 | namespace SDAT { 189 | bool Verify(const std::string& filename); 190 | bool ReadHeader(MemFile& mf, SdatFiles& files); 191 | } 192 | -------------------------------------------------------------------------------- /src/sdatxtract.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "main.h" 5 | #include "sdatxtract.hpp" 6 | #include "sdat.hpp" 7 | #include "common.hpp" 8 | 9 | extern "C" { 10 | #include "swar.h" 11 | #include "decoder/nsstrm.h" 12 | #include "decoder/nsswav.h" 13 | #include "decoder/sseq2mid.h" 14 | } 15 | 16 | std::vector SdatXI_FindSdat(const std::string &filepath) { 17 | std::vector sdats; 18 | uint8_t sdatMagic[] = {'S','D','A','T',0xFF,0xFE,0x00,0x01}; 19 | 20 | std::ifstream ifs(filepath, std::ios::binary | std::ios::ate); 21 | uint32_t mlength = ifs.tellg(); 22 | 23 | if (!ifs.good() || mlength < 0x400) 24 | return sdats; 25 | 26 | MemFile mfile = MemFile(filepath, mlength); 27 | 28 | { 29 | ifs.seekg(0, std::ios::beg); 30 | ifs.read(reinterpret_cast(mfile.GetRawPtr()), mlength); 31 | ifs.close(); 32 | } 33 | 34 | for (uint32_t i = 0; i + 4 < mfile.GetSize(); i++) { 35 | if(!memcmp((mfile.GetRawPtr() + i), sdatMagic, sizeof(sdatMagic))) { 36 | verbose("SDAT found!\n"); 37 | 38 | mfile.m_Offset = i+0xC; 39 | bool p1 = mfile.Read() < 0x100; 40 | mfile.m_Offset = i+0x10; 41 | bool p2 = mfile.Read() < 0x100; 42 | 43 | if (p1 && p2) { 44 | verbose("SDAT confirmed!\n"); 45 | verbose("Reading sdat %d.\n", sdats.size()); 46 | 47 | mfile.m_Offset = i+0x08; 48 | sdats.emplace_back(std::to_string(sdats.size()), mfile.GetRawPtr() + i, mfile.Read()); 49 | } 50 | else { 51 | verbose("SDAT rejected!\n"); 52 | } 53 | } 54 | } 55 | 56 | printf("Total SDATs found: %d\n", sdats.size()); 57 | return sdats; 58 | } 59 | 60 | std::vector SdatX::Init(const std::string &filepath, bool &isNds) { 61 | std::vector sdats; 62 | if (!File::Exists(filepath)) { 63 | error("Failed to open file.\n"); 64 | return sdats; 65 | } 66 | 67 | if (!SDAT::Verify(filepath)) { 68 | isNds = true; 69 | sdats = SdatXI_FindSdat(filepath); 70 | } else 71 | sdats.emplace_back(filepath); 72 | 73 | return sdats; 74 | } 75 | 76 | SdatX::SdatX(const std::string &filepath) : m_Filepath(filepath) 77 | { 78 | if (SDAT::Verify(m_Filepath)) { 79 | std::ifstream ifs(m_Filepath, std::ios::binary | std::ios::ate); 80 | m_Length = ifs.tellg(); 81 | 82 | if (ifs.good()) { 83 | m_File = std::make_shared(m_Filepath, (uint32_t)m_Length); 84 | 85 | ifs.seekg(0, std::ios::beg); 86 | ifs.read(reinterpret_cast(m_File->GetRawPtr()), m_Length); 87 | ifs.close(); 88 | } 89 | } else { 90 | error("Failed to open file.\n"); 91 | } 92 | } 93 | 94 | SdatX::SdatX(const std::string &name, uint8_t* data, uint32_t size) : m_Filepath(name) 95 | { 96 | m_File = std::make_shared(m_Filepath, data, size); 97 | } 98 | 99 | SdatX::~SdatX() 100 | { 101 | m_File = nullptr; 102 | } 103 | 104 | bool SdatX::Write() 105 | { 106 | std::string outfile = "sdat_file_"+std::string(fs::path(m_Filepath).stem().string())+".sdat"; 107 | printf("Outputting to file %s\n", outfile.c_str()); 108 | 109 | std::ofstream ofs(outfile, std::ofstream::binary); 110 | ofs.write(reinterpret_cast(m_File->GetRawPtr()), m_File->GetSize()); 111 | ofs.close(); 112 | 113 | return true; 114 | } 115 | 116 | bool SdatX::Extract() 117 | { 118 | if (!m_File) 119 | return false; 120 | 121 | SdatFiles files; 122 | 123 | printf("Processing file %s\n", m_Filepath.c_str()); 124 | printf("Reading SDAT.\n"); 125 | if (!SDAT::ReadHeader(*m_File, files)) 126 | return false; 127 | 128 | int numSSEQ = 0, numSTRM = 0, numSWAR = 0, numSBNK = 0; 129 | 130 | std::string outdir = fs::path(m_Filepath).stem().string(); 131 | printf("Outputting to directory %s\n", outdir.c_str()); 132 | fs::create_directory(outdir); 133 | fs::current_path(outdir); 134 | 135 | fs::create_directory(DIR_SSEQ); 136 | fs::current_path(DIR_SSEQ); 137 | printf("Writing SSEQ:\n"); 138 | for (uint32_t i = 0; i < files.sseqs.size(); i++) { 139 | SdatSseq sseq = files.sseqs[i]; 140 | 141 | verbose("Prossessing sseq #%d: ", i); 142 | verbose("File %s\n", sseq.FileName.c_str()); 143 | processIndicator(); 144 | 145 | if(bDecodeFile){ 146 | Sseq2mid *nssseq = sseq2midCreate(sseq.Offset, sseq.Length, false); 147 | if(!nssseq){ 148 | printf("SSEQ open error.\n"); 149 | continue; 150 | } 151 | sseq2midSetLoopCount(nssseq, 1); 152 | sseq2midNoReverb(nssseq, false); 153 | if(!sseq2midConvert(nssseq)) { 154 | printf("SSEQ convert error.\n"); 155 | sseq2midDelete(nssseq); 156 | continue; 157 | } 158 | if(!sseq2midWriteMidiFile(nssseq, (sseq.FileName+".mid").c_str())) { 159 | printf("MIDI write error.\n"); 160 | sseq2midDelete(nssseq); 161 | continue; 162 | } 163 | else numSSEQ++; 164 | sseq2midDelete(nssseq); 165 | } else { 166 | std::ofstream ofs(sseq.FileName+".sseq", std::ofstream::binary); 167 | ofs.write(reinterpret_cast(sseq.Offset), sseq.Length); 168 | ofs.close(); 169 | numSSEQ++; 170 | } 171 | } 172 | printf("\n"); 173 | fs::current_path(".."); 174 | 175 | fs::create_directory(DIR_SBNK); 176 | fs::current_path(DIR_SBNK); 177 | printf("Writing SBNK:\n"); 178 | for (uint32_t i = 0; i < files.sbnks.size(); i++) { 179 | SdatSbnk sbnk = files.sbnks[i]; 180 | 181 | verbose("Prossessing sbnk #%d: ", i); 182 | verbose("File %s\n", sbnk.FileName.c_str()); 183 | processIndicator(); 184 | 185 | std::ofstream ofs(sbnk.FileName+".sbnk", std::ofstream::binary); 186 | ofs.write(reinterpret_cast(sbnk.Offset), sbnk.Length); 187 | ofs.close(); 188 | numSBNK++; 189 | } 190 | printf("\n"); 191 | fs::current_path(".."); 192 | 193 | fs::create_directory(DIR_SWAR); 194 | fs::current_path(DIR_SWAR); 195 | printf("Extracting SWAR:\n"); 196 | for (uint32_t i = 0; i < files.swars.size(); i++) { 197 | SdatSwar swar = files.swars[i]; 198 | 199 | verbose("Prossessing Swar #%d: ", i); 200 | verbose("File %s\n", swar.FileName.c_str()); 201 | processIndicator(); 202 | 203 | if(bDecodeFile || bGetSwav){ 204 | fs::create_directory(swar.FileName); 205 | fs::current_path(swar.FileName); 206 | 207 | SWAR swarx; 208 | int ret = 0; 209 | if((ret = SWAREX_init(&swarx, swar.Offset, swar.Length))) { 210 | if (ret == SWARE_BAD) 211 | error("SWAR: Did not pass validation.\nMay be corrupted?\n"); 212 | if (ret == SWARE_EMPTY) 213 | warning("SWAR: No files found to extract.\n"); 214 | 215 | SWAREX_exit(&swarx); 216 | fs::current_path(".."); 217 | continue; 218 | } 219 | 220 | for(uint32_t j = 0; j < swarx.filenum; j++) { 221 | std::string fname = std::to_string(j); 222 | SWAV swav; 223 | verbose("Swav processed %d\n", j); 224 | if(SWAV_genSwav(&swarx.file[j], &swav)){ 225 | printf("SWAV create error.\n"); 226 | SWAV_clear(&swav); 227 | fs::current_path(".."); 228 | continue; 229 | } 230 | if (bDecodeFile) { 231 | NSSwav *nsswav = nsSwavCreate(swav.swavimage, swav.swavsize); 232 | if(!nsswav){ 233 | printf("SWAV open error.\n"); 234 | SWAV_clear(&swav); 235 | fs::current_path(".."); 236 | continue; 237 | } 238 | 239 | if(!nsSwavWriteToWaveFile(nsswav, (fname+".wav").c_str())){ 240 | printf("WAVE write error.\n"); 241 | nsSwavDelete(nsswav); 242 | SWAV_clear(&swav); 243 | fs::current_path(".."); 244 | continue; 245 | } 246 | nsSwavDelete(nsswav); 247 | } else { 248 | std::ofstream ofs(fname+".swav", std::ofstream::binary); 249 | ofs.write(reinterpret_cast(swav.swavimage), swav.swavsize); 250 | ofs.close(); 251 | } 252 | numSWAR++; 253 | SWAV_clear(&swav); 254 | } 255 | SWAREX_exit(&swarx); 256 | fs::current_path(".."); 257 | } else { 258 | std::ofstream ofs(swar.FileName+".swar", std::ofstream::binary); 259 | ofs.write(reinterpret_cast(swar.Offset), swar.Length); 260 | ofs.close(); 261 | numSWAR++; 262 | } 263 | } 264 | printf("\n"); 265 | fs::current_path(".."); 266 | 267 | fs::create_directory(DIR_STRM); 268 | fs::current_path(DIR_STRM); 269 | printf("Writing STRM:\n"); 270 | for (uint32_t i = 0; i < files.strms.size(); i++) { 271 | SdatStrm strm = files.strms[i]; 272 | 273 | verbose("Prossessing strm #%d: ", i); 274 | verbose("File %s\n", strm.FileName.c_str()); 275 | processIndicator(); 276 | 277 | if(bDecodeFile){ 278 | //STRM open 279 | NSStrm *nsstrm = nsStrmCreate(strm.Offset, strm.Length); 280 | if (!nsstrm) { 281 | printf("STRM open error.\n"); 282 | continue; 283 | } 284 | if(!nsStrmWriteToWaveFile(nsstrm, (strm.FileName+".wav").c_str())) { 285 | printf("WAVE write error.\n"); 286 | nsStrmDelete(nsstrm); 287 | continue; 288 | } 289 | else numSTRM++; 290 | nsStrmDelete(nsstrm); 291 | } else { 292 | std::ofstream ofs(strm.FileName+".strm", std::ofstream::binary); 293 | ofs.write(reinterpret_cast(strm.Offset), strm.Length); 294 | ofs.close(); 295 | numSTRM++; 296 | } 297 | } 298 | printf("\n"); 299 | fs::current_path(".."); 300 | 301 | fs::current_path(".."); // outdir 302 | 303 | printf("Total written files:\n"); 304 | printf(" SSEQ:%d\n", numSSEQ); 305 | printf(" SBNK:%d\n", numSBNK); 306 | printf(" %s:%d\n", (bDecodeFile || bGetSwav) ? "SWAV" : "SWAR", numSWAR); 307 | printf(" STRM:%d\n", numSTRM); 308 | 309 | return true; 310 | } 311 | -------------------------------------------------------------------------------- /src/sdatxtract.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "memfile.hpp" 4 | 5 | class SdatX { 6 | public: 7 | 8 | SdatX(const std::string &filepath); 9 | SdatX(const std::string &name, uint8_t* data, uint32_t size); 10 | ~SdatX(); 11 | 12 | bool Extract(); 13 | bool Write(); 14 | 15 | static std::vector Init(const std::string &filepath, bool &isNds); 16 | 17 | private: 18 | 19 | std::string m_Filepath; 20 | std::streamoff m_Length; 21 | 22 | std::shared_ptr m_File = nullptr; 23 | 24 | //std::map Swars; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /src/swar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "swar.h" 8 | 9 | static inline uint32_t getUint(uint8_t* data) { 10 | uint32_t a; 11 | memcpy(&a, data, sizeof(uint32_t)); 12 | return a; 13 | } 14 | 15 | int SWAREX_init(SWAR* swar, uint8_t *image, uint32_t size) { 16 | if(!memcmp(image, "SWAR", 4)){ 17 | swar->swarsize = size; 18 | swar->swarimage = image; 19 | 20 | swar->filenum = getUint(swar->swarimage + 0x38); 21 | swar->file = malloc(sizeof(SWARfile_t) * swar->filenum); 22 | 23 | uint8_t* current_pos = swar->swarimage + 0x3C; 24 | if (swar->filenum) 25 | { 26 | uint32_t i; 27 | for(i = 0; i < swar->filenum - 1; i ++){ 28 | swar->file[i].fileimage = swar->swarimage + getUint(current_pos); 29 | swar->file[i].filesize = getUint(current_pos + 0x04) - getUint(current_pos); 30 | current_pos += 0x04; 31 | } 32 | swar->file[i].fileimage = swar->swarimage + getUint(current_pos); 33 | swar->file[i].filesize = swar->swarsize - getUint(current_pos); 34 | return SWARE_OK; 35 | } 36 | return SWARE_EMPTY; 37 | } 38 | return SWARE_BAD; 39 | } 40 | 41 | void SWAREX_exit(SWAR* swar) { 42 | if (swar->file) free(swar->file); 43 | } 44 | 45 | int SWAV_genSwav(SWARfile_t* file, SWAV* swav) { 46 | swav->swavsize = file->filesize + 0x18; 47 | swav->swavimage = malloc(swav->swavsize); 48 | 49 | memcpy(swav->swavimage, "SWAV", 4); 50 | *(uint32_t*)(swav->swavimage + 0x04) = 0x0100FEFF; 51 | *(uint32_t*)(swav->swavimage + 0x08) = swav->swavsize; 52 | *(uint32_t*)(swav->swavimage + 0x0C) = 0x00010010; 53 | memcpy(swav->swavimage + 0x10, "DATA", 4); 54 | *(uint32_t*)(swav->swavimage + 0x14) = swav->swavsize + 0x08; 55 | memcpy(swav->swavimage + 0x18, file->fileimage, file->filesize); 56 | 57 | return 0; 58 | } 59 | 60 | void SWAV_clear(SWAV* swav) { 61 | if (swav->swavimage) free(swav->swavimage); 62 | } 63 | -------------------------------------------------------------------------------- /src/swar.h: -------------------------------------------------------------------------------- 1 | #ifndef __SX_SWAREX__H__ 2 | #define __SX_SWAREX__H__ 3 | 4 | typedef struct { 5 | uint8_t *fileimage; 6 | uint32_t filesize; 7 | } SWARfile_t; 8 | 9 | typedef struct { 10 | uint8_t *swarimage; 11 | uint32_t swarsize; 12 | SWARfile_t *file; 13 | uint32_t filenum; 14 | } SWAR; 15 | 16 | typedef struct { 17 | uint8_t *swavimage; 18 | uint32_t swavsize; 19 | } SWAV; 20 | 21 | enum swar_error { 22 | SWARE_OK = 0, 23 | SWARE_BAD, 24 | SWARE_EMPTY, 25 | SWARE_GENERIC 26 | }; 27 | 28 | 29 | int SWAREX_init(SWAR* swarfile, uint8_t *image, uint32_t size); 30 | 31 | int SWAV_genSwav(SWARfile_t* swardata, SWAV* swavstruct); 32 | 33 | void SWAV_clear(SWAV* swavstruct); 34 | 35 | void SWAREX_exit(SWAR* swarstruct); 36 | 37 | #endif //__SX_SWAREX__H__ 38 | --------------------------------------------------------------------------------