Last active
January 28, 2019 15:16
-
-
Save consoleaf/3ca2f98c630ed587a94e588d99cae94a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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