2024-07-15 11:46:59 +08:00

302 lines
6.8 KiB
C++

//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Jan 13 08:31:19 PST 1999
// Last Modified: Mon Feb 9 21:26:32 PST 2015 Updated for C++11.
// Filename: tools/vlv.cpp
// URL: https://github.com/craigsapp/midifile/blob/master/tools/vlv.cpp
// Syntax: C++11
// vim: ts=3
//
// Description: Converts Variable Length Values into integers as
// well as converts integers into VLVs.
//
#include "Options.h"
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <string>
using namespace std;
using namespace smf;
#define DECODE 0
#define ENCODE 1
void checkOptions (Options& opts);
void displayVLV (int number, ostream& out);
void example (void);
void printBinary (int number, ostream& out);
void usage (const string& command);
// User interface variables:
Options options; // for command-line processing
int Direction = DECODE; // decoding=0, encoding=1
int InputStyle = 16; // number base of the input (2, 10, or 16)
int OutputStyle = 16; // number base of the output (2, 10, or 16)
vector<int> Input; // storage of input numbers
///////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
options.setOptions(argc, argv);
checkOptions(options);
if (Direction == DECODE) { // decoding
unsigned long output = 0;
for (int i=0; i<(int)Input.size(); i++) {
output = output << 7;
output |= Input[i] & 0x7f;
}
switch (OutputStyle) {
case 2:
printBinary(output, cout);
cout << endl;
break;
case 16:
if (output < 16) {
cout << '0';
}
cout << hex << output << endl;
break;
default:
cout << dec << output << endl;
}
} else { // encoding
displayVLV(Input[0], cout);
cout << endl;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts) {
opts.define("b|2|bin|binary=b", "input is binary");
opts.define("d|10|dec|decimal=b", "input is decimal");
opts.define("B=b", "print binary output");
opts.define("D=b", "print decimal output");
opts.define("author=b", "author of the program");
opts.define("version=b", "version of the program");
opts.define("example=b", "example usage of the program");
opts.define("h|help=b", "list of options for the program");
opts.process();
if (opts.getBoolean("author")) {
cout << "Written by Craig Stuart Sapp, "
<< "craig@ccrma.stanford.edu, January 1999" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << "vlv, version 2.0 (9 Feb 2015)\n"
<< "compiled: " << __DATE__ << endl;
exit(0);
} else if (opts.getBoolean("help")) {
usage(opts.getCommand());
exit(0);
} else if (opts.getBoolean("example")) {
example();
exit(0);
}
// determine input style (the default is hexadecimal)
if (opts.getBoolean("binary")) {
InputStyle = 2;
} else if (opts.getBoolean("decimal")) {
InputStyle = 10;
} else {
InputStyle = 16;
}
// determine output style (the default is hexadecimal)
if (opts.getBoolean("B")) {
OutputStyle = 2;
} else if (opts.getBoolean("D")) {
OutputStyle = 10;
} else {
OutputStyle = 16;
}
if (opts.getArgCount() == 0) {
usage(opts.getCommand());
exit(1);
} else if (opts.getArgCount() == 1) { // encoding into VLV values
Direction = ENCODE;
} else { // decoding from VLV values
Direction = DECODE;
}
// process the input numbers:
int inCount = opts.getArgCount();
if (inCount > 4) {
cout << "Error: VLV values cannot have more than four bytes" << endl;
exit(1);
}
Input.reserve(opts.getArgCount());
Input.resize(0);
for (int i=1; i<=opts.getArgCount(); i++) {
Input.push_back(strtol(opts.getArg(i).c_str(), NULL, InputStyle));
if (Direction == DECODE && i != inCount) {
if (Input.back() < 0x80 || Input.back() > 255) {
cout << "Invalid VLV byte: " << opts.getArg(i) << endl;
exit(1);
}
} else if (Direction == DECODE && i == inCount) {
if (Input.back() < 0 || Input.back() >0x7f) {
cout << "Invalid VLV byte: " << opts.getArg(i) << endl;
exit(1);
}
}
}
}
//////////////////////////////
//
// displayVLV -- prints the number in VLV form
//
void displayVLV(int number, ostream& out) {
unsigned long value = (unsigned long)number;
if (value >= (1 << 28)) {
cout << "Error: number too large to handle" << endl;
exit(1);
}
unsigned long byte[4];
byte[0] = (value >> 21) & 0x7f;
byte[1] = (value >> 14) & 0x7f;
byte[2] = (value >> 7) & 0x7f;
byte[3] = (value >> 0) & 0x7f;
int flag = 0;
for (int i=0; i<3; i++) {
if (byte[i] != 0) {
flag = 1;
}
if (flag) {
byte[i] |= 0x80;
}
}
for (int i=0; i<4; i++) {
if (byte[i] >= 0x80 || i == 3) {
switch (OutputStyle) {
case 2: printBinary(byte[i], out); break;
case 16:
if (byte[i] < 0x0f) {
out << '0';
}
out << hex << byte[i];
break;
default: out << dec << byte[i];
}
if (i != 3) {
cout << ' ';
}
}
}
}
//////////////////////////////
//
// example -- shows various command-line option calls to program.
//
void example(void) {
cout <<
"\n"
"# convert a hex number to VLV bytes:\n"
"vlv 3fff\n"
"\n"
"# convert a hex number to VLV bytes in decimal notation:\n"
"vlv -D 3fff\n"
"\n"
"# convert a decimal number to VLV bytes in decimal notation:\n"
"vlv -d -D 54632\n"
"\n"
"# convert hex VLV bytes into hex number:\n"
"vlv 81 80 F4 00\n"
"\n"
"# convert hex VLV bytes into dec number:\n"
"vlv -D 81 80 F4 00\n"
"\n"
<< endl;
}
//////////////////////////////
//
// printBinary -- prints the number in binary form with no
// leading zeros.
//
void printBinary(int number, ostream& out) {
unsigned long value = (unsigned long) number;
int flag = 0;
for (int i=0; i<32; i++) {
if (value & (1 << (31-i))) {
flag = 1;
}
if (flag) {
if (value & (1 << (31-i))) {
out << '1';
} else {
out << '0';
}
}
}
if (flag == 0) {
out << '0';
}
}
//////////////////////////////
//
// usage -- how to run this program from the command-line.
//
void usage(const string& command) {
cout <<
"\n"
"Convert/Create Variable Length Values.\n"
"\n"
"Usage: " << command << " [-d|-b] [-D|-B] number(s)\n"
"\n"
"Options:\n"
" -d = input numbers are given in decimal notation (hex is default)\n"
" -b = input numbers are given in binary notation (hex is default)\n"
" -D = output numbers are given in decimal notation (hex is default)\n"
" -B = output numbers are given in binary notation (hex is default)\n"
" --options = list all options, default values, and aliases\n"
"\n"
<< endl;
}