Skip to content

Instantly share code, notes, and snippets.

@consoleaf
Last active January 28, 2019 15:16
Show Gist options
  • Save consoleaf/3ca2f98c630ed587a94e588d99cae94a to your computer and use it in GitHub Desktop.
Save consoleaf/3ca2f98c630ed587a94e588d99cae94a to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <ctype.h>
#include <stdbool.h>
char *filepath;
char *prop_name;
char *value;
enum Mode {
SET = 1, GET = 2, SHOW = 3, ERROR = -1, NONE = 0
};
enum Mode mode = NONE;
size_t id3_readSize(FILE *pIobuf);
char *parse_bin_string(char *content, size_t length);
void id3_writeSize(FILE *pIobuf, size_t size);
int main(int argc, char *argv[]) {
static const struct option longopts[] = {
{.name = "filepath", .has_arg = required_argument, .val = 'f'},
{.name = "show", .has_arg = no_argument, .val = 'p'},
{.name = "get", .has_arg = required_argument, .val = 'g'},
{.name = "set", .has_arg = required_argument, .val = 's'},
{.name = "value", .has_arg = required_argument, .val = 'v'},
{},
};
for (;;) {
int opt = getopt_long(argc, argv, "bf", longopts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'f':
fprintf(stderr, "Got --filepath: %s\n", optarg);
filepath = optarg;
break;
case 's':
fprintf(stderr, "Got --set: %s\n", optarg);
prop_name = optarg;
mode = SET;
break;
case 'v':
fprintf(stderr, "Got --value: %s\n", optarg);
if (mode != SET)
mode = ERROR;
value = optarg;
break;
case 'g':
fprintf(stderr, "Got --get: %s\n", optarg);
prop_name = optarg;
if (mode != NONE)
mode = ERROR;
else
mode = GET;
break;
case 'p':
fprintf(stderr, "Got --show\n");
if (mode != NONE)
mode = ERROR;
else
mode = SHOW;
break;
default:
/* Unexpected option */
return 1;
}
}
if (mode == ERROR) {
fprintf(stderr, "Invalid arguments.\n");
return 1;
}
if (!filepath) {
fprintf(stderr, "Filename required.\n");
return 1;
}
FILE *inFile = fopen(filepath, "rb");
if (inFile == NULL) {
fprintf(stderr, "File with specified name is not present.\n");
return 1;
}
size_t tmpSize = 3;
char id3_marker[tmpSize + 1];
id3_marker[tmpSize] = 0x00;
for (int i = 0; i < tmpSize; ++i) {
id3_marker[i] = (char) getc(inFile);
}
if (strcmp(id3_marker, "ID3") != 0) {
fprintf(stderr, "Invalid file\n");
return 1;
}
for (int i = 0; i < 3; ++i) { // Skip subversion etc
getc(inFile);
}
int frameSize = id3_readSize(inFile);
if (mode == SHOW) {
while (ftell(inFile) < frameSize - 10) {
char name[5];
name[4] = 0x00;
for (int i = 0; i < 4; ++i) {
name[i] = (char) getc(inFile);
}
size_t tagSize = id3_readSize(inFile);
// Skip flags
getc(inFile);
getc(inFile);
if (name[0] == 'T' || (strcmp(name, "COMM") == 0)) {
char *content = calloc(sizeof(char), tagSize + 1);
content[tagSize] = 0x00;
content[0] = ' ';
getc(inFile);
for (int i = 0; i < tagSize - 1; ++i) {
content[i] = (char) getc(inFile);
}
char *text = parse_bin_string(content, tagSize - 1);
printf("%s -> %s\n", name, text);
free(text);
free(content);
} else {
fseek(inFile, (long) tagSize, SEEK_CUR);
}
}
}
if (mode == SET) {
bool found = false;
size_t size = 0;
while (ftell(inFile) < frameSize - 10) {
char name[5];
name[4] = 0x00;
for (int i = 0; i < 4; ++i) {
name[i] = (char) getc(inFile);
}
size_t tagSize = id3_readSize(inFile);
// Skip flags
getc(inFile);
getc(inFile);
if (strcmp(name, prop_name) == 0) {
found = true;
size = tagSize;
} else {
fseek(inFile, (long) tagSize, SEEK_CUR);
}
if (found)
break;
}
if (!found || size == 0) {
printf("Prop %s is not present in the file.\n", prop_name);
return 0;
}
int pos = ftell(inFile) - 6; // Remember the tag start
fseek(inFile, 0, SEEK_SET); // Reset cursor to copy the file
FILE *outFile = fopen("newTmpFile.mp3", "wb");
for (int i = 0; i < 6; i++) // Copy header
{
putc((char) getc(inFile), outFile);
}
size_t newSize = frameSize - size + strlen(value); // Calculate and write new len
id3_writeSize(outFile, newSize);
fseek(inFile, ftell(outFile), SEEK_SET); // Sync the two files until THE frame
while (ftell(outFile) != pos)
putc((char) getc(inFile), outFile);
// Write frame len
id3_writeSize(outFile, strlen(value) + 1);
// Flags and encoding (set to ASCII)
for (int i = 0; i < 3; ++i) {
putc(0x00, outFile);
}
for(int i = 0; i < strlen(value); i++)
putc(value[i] & 0xFF, outFile);
fseek(inFile, 4 + 2 + size, SEEK_CUR);
while(1)
{
char c = (char)getc(inFile);
if(c == EOF)
if(feof(inFile) != 0)
break;
putc(c, outFile);
}
fclose(inFile);
fclose(outFile);
remove(filepath);
rename("newTmpFile.mp3", filepath);
}
if (mode == GET) {
bool found = false;
while (ftell(inFile) < frameSize - 10) {
char name[5];
name[4] = 0x00;
for (int i = 0; i < 4; ++i) {
name[i] = (char) getc(inFile);
}
size_t tagSize = id3_readSize(inFile);
// Skip flags
getc(inFile);
getc(inFile);
if (strcmp(name, prop_name) == 0) {
found = true;
char *content = calloc(sizeof(char), tagSize + 1);
content[tagSize] = 0x00;
content[0] = ' ';
getc(inFile);
for (int i = 0; i < tagSize - 1; ++i) {
content[i] = (char) getc(inFile);
}
char *text = parse_bin_string(content, tagSize - 1);
printf("%s -> %s\n", name, text);
free(text);
free(content);
} else {
fseek(inFile, (long) tagSize, SEEK_CUR);
}
}
if (!found)
printf("Prop %s is not present in the file.\n", prop_name);
}
return 0;
}
void id3_writeSize(FILE *pIobuf, size_t size) {
char buf[4];
for (int i = 0; i < 4; i++) {
buf[i] = (char) ((size >> ((3 - i) * 7)) & 0xFF);
buf[i] = (char) (buf[i] & 0x7F);
putc(buf[i], pIobuf);
}
}
char *parse_bin_string(char *content, size_t length) {
char *result = calloc(sizeof(char), length);
int i = 0;
for (int j = 0; j < length; ++j) {
if (isprint(content[j]))
result[i++] = content[j];
}
return result;
}
size_t id3_readSize(FILE *pIobuf) {
size_t res = 0;
char tmp;
for (int i = 0; i < 4; ++i) {
tmp = (char) getc(pIobuf);
res = res << 7;
res |= tmp;
}
return res;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment