patterncppMinor
Converting GTA Vice City ADF audio files to MP3
Viewed 0 times
vicegtaadfaudiofilesmp3cityconverting
Problem
I wrote a tiny command line tool that converts ADF audio files from the
Grand Theft Auto - Vice City game to MP3 files that can be played outside the game.
The ADF file format stores the game soundtracks and radio stations. These in turn
are just MP3 files that had every byte XORed with the decimal constant
My application is very simple, it only takes one or two file names
and processes every byte of the input file, producing a MP3 as output.
Usage example:
Looks for the file "flash.adf" in the current directory and writes a
file named "flash.mp3" to the current directory as its output. You can
also provide an explicit output filename as the third parameter.
adf2mp3.cpp:
```
// Standard C++ includes:
#include
#include
#include
#include
#include
#include
// Standard C / POSIX includes:
#include
#include
#include
namespace adf2mp3
{
using ubyte = unsigned char;
using ulong = unsigned long;
//
// The GTA Vice City ADF files are MP3 files that
// had each byte XORed with this magic constant.
// 34 is 22 in hexadecimal and 42 in octal...
//
// Not sure who figured this out, but I got this
// info from the Xentax File Format Wiki.
//
constexpr ubyte GtaMagic = 34;
//
// Process the input file in chunks of this size in bytes.
// The chunk buffer is allocated dynamically (it is a std::vector)
// so this can be a fairly large value.
//
constexpr ulong ChunkSize = 8192;
// ========================================================
struct FileStats
{
ulong fileLength; // File length in bytes.
bool isDirectory; // True if the path provided pointed to a directory.
bool isNormalFile; // True is the path provided pointed to a file.
};
FileStats fileStatsForPath(const std::string & pathname)
{
errno = 0; // Clear the errno global, just in case.
struct stat statBuf;
if (stat(pathname.c_str(), &statBuf) != 0)
{
throw std::runtime_error("Path '" + pathname + "': " + std::string(std::stre
Grand Theft Auto - Vice City game to MP3 files that can be played outside the game.
The ADF file format stores the game soundtracks and radio stations. These in turn
are just MP3 files that had every byte XORed with the decimal constant
34.My application is very simple, it only takes one or two file names
and processes every byte of the input file, producing a MP3 as output.
Usage example:
$ ./adf2mp3 flash.adfLooks for the file "flash.adf" in the current directory and writes a
file named "flash.mp3" to the current directory as its output. You can
also provide an explicit output filename as the third parameter.
adf2mp3.cpp:
```
// Standard C++ includes:
#include
#include
#include
#include
#include
#include
// Standard C / POSIX includes:
#include
#include
#include
namespace adf2mp3
{
using ubyte = unsigned char;
using ulong = unsigned long;
//
// The GTA Vice City ADF files are MP3 files that
// had each byte XORed with this magic constant.
// 34 is 22 in hexadecimal and 42 in octal...
//
// Not sure who figured this out, but I got this
// info from the Xentax File Format Wiki.
//
constexpr ubyte GtaMagic = 34;
//
// Process the input file in chunks of this size in bytes.
// The chunk buffer is allocated dynamically (it is a std::vector)
// so this can be a fairly large value.
//
constexpr ulong ChunkSize = 8192;
// ========================================================
struct FileStats
{
ulong fileLength; // File length in bytes.
bool isDirectory; // True if the path provided pointed to a directory.
bool isNormalFile; // True is the path provided pointed to a file.
};
FileStats fileStatsForPath(const std::string & pathname)
{
errno = 0; // Clear the errno global, just in case.
struct stat statBuf;
if (stat(pathname.c_str(), &statBuf) != 0)
{
throw std::runtime_error("Path '" + pathname + "': " + std::string(std::stre
Solution
Your program is overly complicated. That's my first thought I've had while reading your code. Don't overly complicate your problem but KISS.
-
You don't need to care whether you're trying to open a directory: you just can't open a directory as a file.
-
Following, you don't need
-
You should be throwing a
-
You don't need a namespace.
-
In
-
In
-
To get the file size, you can:
-
I would avoid
Details
-
You don't need to care whether you're trying to open a directory: you just can't open a directory as a file.
-
Following, you don't need
FileStats.- => You don't need
stat.
-
You should be throwing a
system_error ( : public runtime_error ) which will provide a modern way to retrieve error messages (can be used in a cross-platform manner, too).-
You don't need a namespace.
-
In
printHelpText consider that each cout is a different call. You can chain them in just one thanks to string literal chaining (done by the preprocessor) and cascading calls:std::cout [output_file]\n"
"Runs the tool normally. If the output filename is not provided\n"
"the input filename is used but the extension is replaced with '.mp3'.\n"
"\n";
// ...-
In
processFiles you're still complicating your life. A vector is "overkill": you just need an unique_ptr.-
To get the file size, you can:
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
auto size = in.tellg();
in.seekp(ios::begin);-
I would avoid
adf2mp3::run and, in general, run/init functions in the global namespace: that's what main is supposed to do. Details
- If you want to be pedantic,
unsigned charmay be larger than 8 bits (incredibly uncommon nowadays): you'd better usestd::uint8_t.
- I don't think a loop is needed as it should write it all in one time (unlike low-level I/O).
- Setting
errnoto zero isn't really necessary (as you stated) because you're supposed to check it only if a (C) function returns a value indicating a failure and sets it accordingly.
- I'm not sure if you can use
const char *argv[]but the legal signatures ofmainare described here.
Code Snippets
std::cout << "\n"
"Usage:\n"
"$ " << programName << " <input_file> [output_file]\n"
"Runs the tool normally. If the output filename is not provided\n"
"the input filename is used but the extension is replaced with '.mp3'.\n"
"\n";
// ...std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
auto size = in.tellg();
in.seekp(ios::begin);Context
StackExchange Code Review Q#75011, answer score: 3
Revisions (0)
No revisions yet.