Skip to content

Instantly share code, notes, and snippets.

@fdeitylink
Last active July 1, 2017 01:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fdeitylink/18ddc14f3a716cbecad6510bb3c3dc2a to your computer and use it in GitHub Desktop.
Save fdeitylink/18ddc14f3a716cbecad6510bb3c3dc2a to your computer and use it in GitHub Desktop.
Converts Cave Story TSC files to plaintext files and back
/*
TSC Converter v1.0
A command-line program that converts Cave Story TSC files to plaintext files and back
Created by FDeityLink
Released under public domain
-- Must be compiled with the C++11 standard --
Instructions for use:
-Supply a file directory as an argument or in the program itself - the program auto-detects format and converts accordingly.
-If the filedir has spaces, you needn't put it in quotes (unless it's an argument)
-If you do it as an argument, then you can put in as many file directories as you wish
-However, this does not work for files given in the program itself
-If you wish to exit, type "exit"
TSC File Format Notes:
Encoding char is the middle character in file: char at floor(file size / 2)
ex: size of 313 bytes, encode char is at 156th byte
Newlines are Windows-style newlines ("\r\n"): 0x0D + encode char val, then 0x0A + encode char val
- Because they're Windows-style newlines, open your files in binary mode, not text, otherwise the following transformations are done, which will end up corrupting the file
- '\r\n' --> "\n" for input
- '\n' --> "\r\n" for output
All characters in the file except for the encoding char itself (so other copies of the character are still encoded) have the value of the encoding char added to them when saved
ex: encoding char = [space]: only that instance of the [space] character will not be encoded, but the others will
When reading a TSC file, subtract the encoding character from each character (except the encoding char itself, as noted above) to properly decode the file
When writing a TSC file, add the encoding character to each character (except the encoding char itself, as noted above) to properly encode the file
*/
#include <iostream>
#include <string>
#include <fstream>
#include <algorithm> //std::remove(ForwardIterator first, ForwardIterator last, const T& val)
#include <cstdio> //remove(const char* filename)
#define EXTENSION_LENGTH 4
int main(const int argc, const char* argv[]) {
using namespace std;
if (argc < 1) { //signed int, possible error with it holding zero or a negative value
throw out_of_range("Argument count must be greater than or equal to 1 (including executable name). Try again.");
}
cout << "TSC Converter v1.0" << endl;
cout << "Created by F_Deity_Link\n" << endl;
for (int i = 1;;) {
//Parsing input and setting up variables for later
string originalDir;
if (i < argc) {
originalDir = argv[i++];
cout << originalDir << endl;
}
else {
cout << "Enter the path of a TSC or text file you wish to convert, or type \"exit\" to quit" << endl;
while ("" == originalDir) {
getline(cin, originalDir);
}
}
if (string("exit") == originalDir) {
return 0;
}
originalDir.erase(std::remove(originalDir.begin(), originalDir.end(), '\"'), originalDir.end()); //takes out quote marks
//std::remove to ensure no conflict with remove(), which deletes a file
bool originalIsTscFile = false;
string convertedExtension = ".tsc";
if (originalDir.length() >= EXTENSION_LENGTH) { //if length < EXTENSION_LENGTH, std::out_of_range thrown when passing (str.length() - EXTENSION_LENGTH) to compare()
if (originalDir.compare(originalDir.length() - EXTENSION_LENGTH, EXTENSION_LENGTH, string(".tsc")) == 0) {
originalIsTscFile = true;
convertedExtension = ".txt";
}
else if (originalDir.compare(originalDir.length() - EXTENSION_LENGTH, EXTENSION_LENGTH, string(".txt")) != 0) {
cerr << "ERROR: Unsupported file type\n" << endl;
continue;
}
}
else {
cerr << "ERROR: Unsupported file type\n" << endl;
continue;
}
//Opening files
ifstream originalFile(originalDir, ifstream::binary | ifstream::ate); //ate (at end) starts at end (so I don't need to seek before finding encoder pos)
if (!originalFile) {
cerr << "ERROR: Could not open file \"" << originalDir << "\"\n" << endl;
continue;
}
string convertedDir = originalDir;
convertedDir.replace(convertedDir.length() - EXTENSION_LENGTH, EXTENSION_LENGTH, convertedExtension); //change the extension
ofstream convertedFile(convertedDir, ofstream::binary | ofstream::trunc);
if (!convertedFile) {
cerr << "ERROR: Could not open file \"" << convertedDir << "\"\n" << endl;
continue;
}
//Converting the file
cout << "Converting \"" << originalDir << "\" and writing it out to \"" << convertedDir << "\"" << endl;
const int encoderCharPos = originalFile.tellg() / 2; //integer division, will truncate decimal
originalFile.seekg(encoderCharPos);
unsigned char encoderChar;
originalFile >> noskipws >> encoderChar;
if (originalIsTscFile) { //subtract to decode, add to encode
encoderChar *= -1;
}
originalFile.seekg(0, ios_base::beg);
for (unsigned char c; originalFile >> noskipws >> c;) {
if (encoderCharPos + 1 != originalFile.tellg()) {
c += encoderChar;
}
convertedFile << c;
}
if (originalFile.bad() || convertedFile.bad()) {
cerr << "ERROR: An error occurred with converting \"" << originalDir << "\" to \"" << convertedDir << "\". Please try again\n" << endl;
convertedFile.close();
remove(convertedDir.c_str());
}
else {
cout << "\"" << originalDir << "\" was converted to \"" << convertedDir << "\"\n" << endl;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment