Added files

This commit is contained in:
2025-11-29 13:18:54 +03:00
parent 8e0e50fac1
commit a3dbb1b169
2 changed files with 296 additions and 2 deletions

View File

@@ -1,2 +1,97 @@
# ccd2cue
Bruh, just a ccd -> cue converter
# CCD2CUE - Конвертер CCD в CUE файлы
## Описание
`ccd2cue` - Утилита для преобразования файлов образов CD/DVD в формате CCD (CloneCD) в CUE-файлы.
## Особенности
- Конвертирует CCD-файлы в CUE-формат
- Автоматически определяет файлы образов (.img, .bin, .iso)
- Поддерживает различные типы треков (данные и аудио)
- Корректно обрабатывает временные метки и индексы треков
- Не требует внешних зависимостей (кроме стандартной библиотеки C)
## Установка и компиляция
### Требования
- Компилятор GCC
- Стандартная библиотека C
- POSIX-совместимая операционная система
### Компиляция
```bash
gcc -o ccd2cue ccd2cue.c
```
## Использование
### Базовый синтаксис
```bash
ccd2cue -c <файл.ccd>
```
### Параметры командной строки
| Параметр | Описание |
|----------|----------|
| `-c <файл>` | Обязательный параметр. Указывает путь к входному CCD-файлу |
### Примеры использования
```bash
# Конвертировать файл example.ccd
./ccd2cue -c example.ccd
# Конвертировать файл в другом каталоге
./ccd2cue -c /path/to/disc.ccd
```
## Входные и выходные файлы
### Входные файлы
- **CCD-файл**: Файл с расширением `.ccd`, содержащий метаданные о структуре диска
- **Файл образа**: Автоматически определяется по расширениям:
- `.img`
- `.bin`
- `.iso`
### Выходные файлы
- **CUE-файл**: Создается с тем же именем, что и CCD-файл, но с расширением `.cue`
### Пример структуры файлов
```
директория_проекта/
├── disc.ccd # Входной файл
├── disc.img # Файл образа (автоопределение)
└── disc.cue # Выходной файл (создается программой)
```
## Форматы поддерживаемых треков
Программа поддерживает два основных типа треков:
| Тип трека | Описание | CUE-формат |
|-----------|----------|------------|
| Данные | Треки с компьютерными данными | `MODE1/2352` |
| Аудио | Аудио треки | `AUDIO` |
## Обработка временных меток
Программа автоматически корректирует временные метки:
- Вычитает 2 секунды из времени каждого трека
- Корректно обрабатывает переходы через минуту
- Сохраняет точность до кадров (frames)
## Коды возврата
| Код | Значение |
|-----|----------|
| `0` | Успешное выполнение |
| `1` | Ошибка: не указан CCD-файл |
| `2` | Ошибка: не удалось прочитать CCD-файл |
| `3` | Ошибка: не найден файл образа |

199
ccd2cue.c Normal file
View File

@@ -0,0 +1,199 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <dirent.h>
#include <ctype.h>
#define MAX_LINE_LEN 256
#define MAX_FILENAME_LEN 256
#define MAX_SECTIONS 100
typedef struct {
char name[MAX_LINE_LEN];
char control[MAX_LINE_LEN];
char session[MAX_LINE_LEN];
char pmin[MAX_LINE_LEN];
char psec[MAX_LINE_LEN];
char pframe[MAX_LINE_LEN];
char plba[MAX_LINE_LEN];
} Section;
typedef struct {
Section sections[MAX_SECTIONS];
int count;
} Config;
void config_init(Config *config) {
config->count = 0;
}
int is_section_line(const char *line) {
return line[0] == '[' && strchr(line, ']') != NULL;
}
void parse_section_name(const char *line, char *section_name) {
const char *start = strchr(line, '[') + 1;
const char *end = strchr(line, ']');
strncpy(section_name, start, end - start);
section_name[end - start] = '\0';
}
void trim_whitespace(char *str) {
char *end;
while (isspace((unsigned char)*str)) str++;
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
}
int parse_config_file(const char *filename, Config *config) {
FILE *file = fopen(filename, "r");
if (!file) return -1;
char line[MAX_LINE_LEN];
int current_section = -1;
while (fgets(line, sizeof(line), file)) {
trim_whitespace(line);
if (strlen(line) == 0) continue;
if (is_section_line(line)) {
current_section = config->count++;
if (current_section >= MAX_SECTIONS) break;
parse_section_name(line, config->sections[current_section].name);
continue;
}
if (current_section >= 0) {
char *sep = strchr(line, '=');
if (!sep) continue;
*sep = '\0';
char *key = line;
char *value = sep + 1;
trim_whitespace(key);
trim_whitespace(value);
Section *sec = &config->sections[current_section];
if (strcmp(key, "Control") == 0) strcpy(sec->control, value);
else if (strcmp(key, "Session") == 0) strcpy(sec->session, value);
else if (strcmp(key, "PMin") == 0) strcpy(sec->pmin, value);
else if (strcmp(key, "PSec") == 0) strcpy(sec->psec, value);
else if (strcmp(key, "PFrame") == 0) strcpy(sec->pframe, value);
else if (strcmp(key, "PLBA") == 0) strcpy(sec->plba, value);
}
}
fclose(file);
return 0;
}
int find_image_file(char *found_name) {
const char *extensions[] = {".img", ".bin", ".iso", NULL};
DIR *dir = opendir(".");
if (!dir) return -1;
struct dirent *entry;
while ((entry = readdir(dir))) {
if (entry->d_type != DT_REG) continue;
const char *name = entry->d_name;
const char *dot = strrchr(name, '.');
if (!dot) continue;
for (int i = 0; extensions[i]; i++) {
if (strcasecmp(dot, extensions[i]) == 0) {
strcpy(found_name, name);
closedir(dir);
return 0;
}
}
}
closedir(dir);
return -1;
}
void write_cue_file(const char *ccd_file, const Config *config, const char *img_file) {
char cue_file[MAX_FILENAME_LEN];
strcpy(cue_file, ccd_file);
char *dot = strrchr(cue_file, '.');
if (dot) *dot = '\0';
strcat(cue_file, ".cue");
FILE *cue_fp = fopen(cue_file, "w");
if (!cue_fp) {
perror("Failed to create CUE file");
return;
}
fprintf(cue_fp, "FILE \"%s\" BINARY\n", img_file);
int track_counter = 0;
int begin_found = 0;
for (int i = 0; i < config->count; i++) {
const Section *sec = &config->sections[i];
if (strstr(sec->name, "Entry") != sec->name) continue;
int plba = atoi(sec->plba);
if (plba == 0) begin_found = 1;
if (!begin_found) continue;
track_counter++;
int minute = atoi(sec->pmin);
int second = atoi(sec->psec);
int frame = atoi(sec->pframe);
if (second == 0) {
if (minute >= 1) {
minute--;
second = 60;
}
}
second -= 2;
const char *track_type = strcmp(sec->control, "0x04") == 0 ? "MODE1/2352" : "AUDIO";
int track_index = atoi(sec->session);
fprintf(cue_fp, " TRACK %02d %s\n", track_counter, track_type);
fprintf(cue_fp, " INDEX %02d %02d:%02d:%02d\n",
track_index, minute, second, frame);
}
fclose(cue_fp);
}
int main(int argc, char *argv[]) {
char *ccd_file = NULL;
int opt;
while ((opt = getopt(argc, argv, "c:")) != -1) {
if (opt == 'c') ccd_file = optarg;
}
if (!ccd_file) {
fprintf(stderr, "Usage: %s -c <ccd_file>\n", argv[0]);
return 1;
}
Config config;
config_init(&config);
if (parse_config_file(ccd_file, &config) != 0) {
fprintf(stderr, "Failed to parse CCD file\n");
return 1;
}
char img_file[MAX_FILENAME_LEN] = "";
if (find_image_file(img_file) != 0) {
fprintf(stderr, "No image file found\n");
return 1;
}
write_cue_file(ccd_file, &config, img_file);
return 0;
}