├── README.md ├── version.h ├── .gitignore ├── Makefile ├── maketmd_license.txt └── maketmd.cpp /README.md: -------------------------------------------------------------------------------- 1 | # maketmd 2 | Title Metadata (TMD) Creator for DSiWare Homebrew 3 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | #define TMD_CREATOR_VER "0.2" 2 | #define TMD_CREATOR_DATE "built on Thu, Jun 7, 2018 6:37:01 PM" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # MacOS annoying file 35 | .DS_Store 36 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | CFLAGS = 3 | LFLAGS = -lcrypto 4 | OBJECTS = maketmd.o 5 | TARGET_NAME = maketmd 6 | VERSION_VER = 0.2 7 | VERSION_DATE = built on `date` 8 | 9 | ifeq ($(OS),Windows_NT) 10 | TARGET = $(TARGET_NAME).exe 11 | LFLAGS += -static -static-libgcc 12 | else 13 | UNAME_S := $(shell uname -s) 14 | ifeq ($(UNAME_S),Darwin) 15 | CFLAGS += -I/usr/local/opt/openssl/include 16 | LFLAGS += -L/usr/local/opt/openssl/lib 17 | endif 18 | TARGET = $(TARGET_NAME) 19 | endif 20 | 21 | all: $(TARGET) 22 | 23 | version.h: Makefile 24 | @printf "#define TMD_CREATOR_VER \"%s\"\r\n" "$(VERSION_VER)" > version.h 25 | @printf "#define TMD_CREATOR_DATE \"%s\"\r\n" "$(VERSION_DATE)" >> version.h 26 | 27 | %.o: %.cpp version.h 28 | $(CXX) $(CFLAGS) -c $< -o $@ 29 | 30 | $(TARGET): $(OBJECTS) 31 | $(CXX) $(CFLAGS) $^ -o $@ $(LFLAGS) 32 | 33 | run: $(TARGET) 34 | @./$(TARGET) test/00000000.app test/title.tmd 35 | 36 | clean: 37 | rm -f $(OBJECTS) $(TARGET) version.h 38 | -------------------------------------------------------------------------------- /maketmd_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 2 | Przemyslaw Skryjomski (Tuxality) 3 | 4 | Big thanks to: 5 | Apache Thunder 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any 9 | damages arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any 12 | purpose, including commercial applications, and to alter it and 13 | redistribute it freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you 16 | must not claim that you wrote the original software. If you use 17 | this software in a product, an acknowledgment in the product 18 | documentation would be appreciated but is not required. 19 | 20 | 2. Altered source versions must be plainly marked as such, and 21 | must not be misrepresented as being the original software. 22 | 23 | 3. This notice may not be removed or altered from any source 24 | distribution. -------------------------------------------------------------------------------- /maketmd.cpp: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------- 2 | 3 | maketmd.cpp -- TMD Creator for DSiWare Homebrew 4 | 5 | Copyright (C) 2018 6 | Przemyslaw Skryjomski (Tuxality) 7 | 8 | Big thanks to: 9 | Apache Thunder 10 | 11 | This software is provided 'as-is', without any express or implied 12 | warranty. In no event will the authors be held liable for any 13 | damages arising from the use of this software. 14 | 15 | Permission is granted to anyone to use this software for any 16 | purpose, including commercial applications, and to alter it and 17 | redistribute it freely, subject to the following restrictions: 18 | 19 | 1. The origin of this software must not be misrepresented; you 20 | must not claim that you wrote the original software. If you use 21 | this software in a product, an acknowledgment in the product 22 | documentation would be appreciated but is not required. 23 | 24 | 2. Altered source versions must be plainly marked as such, and 25 | must not be misrepresented as being the original software. 26 | 27 | 3. This notice may not be removed or altered from any source 28 | distribution. 29 | 30 | ---------------------------------------------------------------------------------*/ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #ifdef _WIN32 41 | uint32_t __bswap_32(uint32_t value) { 42 | return _byteswap_ulong(value); 43 | } 44 | #elif __APPLE__ 45 | #include 46 | uint32_t __bswap_32(uint32_t value) { 47 | return OSSwapInt32(value); 48 | } 49 | #else 50 | #include 51 | #endif // _WIN32 52 | 53 | #include "version.h" 54 | 55 | #define TMD_SIZE 0x208 56 | #define SHA_BUFFER_SIZE 0x200 57 | 58 | void tmd_create(uint8_t* tmd, std::fstream& app) { 59 | // Phase 1 - offset 0x18C (Title ID, first part) 60 | { 61 | app.seekg(0x234, app.beg); 62 | 63 | uint32_t value; 64 | app.read((char*)&value, 4); 65 | value = __bswap_32(value); 66 | 67 | memcpy(tmd + 0x18C, &value, 4); 68 | } 69 | 70 | // Phase 2 - offset 0x190 (Title ID, second part) 71 | { 72 | // We can take this also from 0x230, but reversed 73 | app.seekg(0x0C, app.beg); 74 | app.read((char*)&tmd[0x190], 4); 75 | } 76 | 77 | // Phase 3 - offset 0x198 (Group ID = '01') 78 | { 79 | app.seekg(0x10, app.beg); 80 | app.read((char*)&tmd[0x198], 2); 81 | } 82 | 83 | // Phase 4 - offset 0x1AA (fill-in 0x80 value, 0x10 times) 84 | { 85 | for(size_t i = 0; i<0x10; i++) { 86 | tmd[0x1AA + i] = 0x80; 87 | } 88 | } 89 | 90 | // Phase 5 - offset 0x1DE (number of contents = 1) 91 | { 92 | tmd[0x1DE] = 0x00; 93 | tmd[0x1DF] = 0x01; 94 | } 95 | 96 | // Phase 6 - offset 0x1EA (type of content = 1) 97 | { 98 | tmd[0x1EA] = 0x00; 99 | tmd[0x1EB] = 0x01; 100 | } 101 | 102 | // Phase 7 - offset, 0x1EC (file size, 8B) 103 | { 104 | app.seekg(0, app.end); 105 | uint32_t size = app.tellg(); 106 | size = __bswap_32(size); 107 | 108 | // We only use 4B for size as for now 109 | memcpy((tmd + 0x1F0), &size, sizeof(uint32_t)); 110 | } 111 | 112 | // Phase 8 - offset, 0x1F4 (SHA1 sum, 20B) 113 | { 114 | // Makes use of OpenSSL library 115 | app.seekg(0, app.beg); 116 | 117 | uint8_t buffer[SHA_BUFFER_SIZE] = { 0 }; 118 | uint32_t buffer_read = 0; 119 | 120 | SHA_CTX ctx; 121 | SHA1_Init(&ctx); 122 | 123 | do { 124 | app.read((char*)&buffer[0], SHA_BUFFER_SIZE); 125 | buffer_read = app.gcount(); 126 | 127 | SHA1_Update(&ctx, buffer, buffer_read); 128 | } while(buffer_read == SHA_BUFFER_SIZE); 129 | 130 | SHA1_Final(buffer, &ctx); 131 | 132 | // Store SHA1 sum 133 | memcpy((tmd + 0x1F4), buffer, SHA_DIGEST_LENGTH); 134 | } 135 | } 136 | 137 | int main(int argc, char* argv[]) { 138 | printf("TMD Creator for DSiWare Homebrew %s - %s\n", TMD_CREATOR_VER, TMD_CREATOR_DATE); 139 | printf("by Przemyslaw Skryjomski (Tuxality)\n"); 140 | 141 | if(argc < 2) { 142 | printf("\nUsage: %s file.app \n", argv[0]); 143 | return 1; 144 | } 145 | 146 | // APP file (input) 147 | std::fstream app(argv[1], std::ios::in | std::ios::binary); 148 | 149 | if(!app.is_open()) { 150 | printf("Error at opening %s for reading.\n", argv[1]); 151 | return 1; 152 | } 153 | 154 | // TMD file (output) 155 | std::string tmdPath = "title.tmd"; 156 | if(argc > 2) { 157 | tmdPath = argv[2]; 158 | } 159 | 160 | std::fstream tmd(tmdPath, std::ios::out | std::ios::binary); 161 | 162 | if(!tmd.is_open()) { 163 | printf("Error at opening %s for writing.\n", argv[2]); 164 | return 1; 165 | } 166 | 167 | // Allocate memory for TMD 168 | uint8_t* tmd_template = new uint8_t[TMD_SIZE](); // zeroed 169 | 170 | // Prepare TMD template then write to file 171 | tmd_create(tmd_template, app); 172 | tmd.write((const char*)(&tmd_template[0]), TMD_SIZE); 173 | 174 | // Free allocated memory for TMD 175 | delete[] tmd_template; 176 | 177 | // This is done in dtor, but we additionally flush tmd. 178 | app.close(); 179 | tmd.flush(); 180 | tmd.close(); 181 | 182 | return 0; 183 | } 184 | --------------------------------------------------------------------------------