154 lines
4.3 KiB
C
154 lines
4.3 KiB
C
/*
|
|
* sap2ntsc.c - convert PAL SAP files to NTSC
|
|
*
|
|
* Copyright (C) 2012-2019 Piotr Fusik
|
|
*
|
|
* This file is part of ASAP (Another Slight Atari Player),
|
|
* see http://asap.sourceforge.net
|
|
*
|
|
* ASAP is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published
|
|
* by the Free Software Foundation; either version 2 of the License,
|
|
* or (at your option) any later version.
|
|
*
|
|
* ASAP is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with ASAP; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* public API doesn't allow us to set NTSC or FASTPLAY */
|
|
#include "asap.c"
|
|
|
|
static void fatal_error(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
fprintf(stderr, "sap2ntsc: ");
|
|
vfprintf(stderr, format, args);
|
|
fputc('\n', stderr);
|
|
va_end(args);
|
|
exit(1);
|
|
}
|
|
|
|
static void process_file(const char *filename)
|
|
{
|
|
/* check filename */
|
|
const char *ext = strrchr(filename, '.');
|
|
if (ext == NULL || strcasecmp(ext, ".sap") != 0)
|
|
fatal_error("%s: filename must be *.sap", filename);
|
|
|
|
/* read file */
|
|
FILE *fp = fopen(filename, "rb");
|
|
if (fp == NULL)
|
|
fatal_error("cannot open %s", filename);
|
|
static unsigned char module[ASAPInfo_MAX_MODULE_LENGTH];
|
|
int module_len = fread(module, 1, sizeof(module), fp);
|
|
fclose(fp);
|
|
|
|
/* parse file */
|
|
ASAPInfo *info = ASAPInfo_New();
|
|
if (info == NULL)
|
|
fatal_error("out of memory");
|
|
if (!ASAPInfo_Load(info, filename, module, module_len))
|
|
fatal_error("%s: unsupported file", filename);
|
|
|
|
/* check if conversion possible */
|
|
if (ASAPInfo_IsNtsc(info))
|
|
fatal_error("%s: is already NTSC", filename);
|
|
if (ASAPInfo_GetPlayerRateScanlines(info) != 312)
|
|
fatal_error("%s: uses FASTPLAY", filename);
|
|
|
|
/* do the conversion */
|
|
ASAPWriter *writer = ASAPWriter_New();
|
|
if (writer == NULL)
|
|
fatal_error("out of memory");
|
|
info->ntsc = true;
|
|
info->fastplay = 262;
|
|
for (int i = 0; i < ASAPInfo_GetSongs(info); i++) {
|
|
int duration = ASAPInfo_GetDuration(info, i);
|
|
if (duration > 0)
|
|
ASAPInfo_SetDuration(info, i, (int) (duration * (1773447 / 1789772.5 * 262 / 312)));
|
|
}
|
|
static unsigned char output[ASAPInfo_MAX_MODULE_LENGTH];
|
|
ASAPWriter_SetOutput(writer, output, 0, sizeof(output));
|
|
int output_len = ASAPWriter_Write(writer, filename, info, module, module_len, true);
|
|
ASAPWriter_Delete(writer);
|
|
if (output_len < 0)
|
|
fatal_error("%s: conversion error", filename);
|
|
|
|
/* write file */
|
|
fp = fopen(filename, "wb");
|
|
if (fp == NULL)
|
|
fatal_error("cannot write %s", filename);
|
|
if (fwrite(output, output_len, 1, fp) != 1) {
|
|
fclose(fp);
|
|
remove(filename); /* "unlink" is less portable */
|
|
fatal_error("%s: write error", filename);
|
|
}
|
|
fclose(fp);
|
|
|
|
/* print summary */
|
|
printf("%s: ", filename);
|
|
int warnings = 0;
|
|
|
|
/* issue a warning for samples - they may break on NTSC */
|
|
/* TYPE S has FASTPLAY!=312, so it has been rejected earlier */
|
|
if (ASAPInfo_GetTypeLetter(info) == 'D') {
|
|
printf("WARNING: TYPE D");
|
|
warnings++;
|
|
}
|
|
|
|
/* issue a warning if the 6502 code possibly reads the PAL/NTSC flag of GTIA
|
|
(LDA/LDX/LDY $D014, e.g. Ghostbusters.sap).
|
|
This is just a guess - false positives are possible and other code may be used for NTSC detection. */
|
|
for (int i = 0; i < module_len - 2; i++) {
|
|
if (module[i] >= 0xac && module[i] <= 0xae && module[i + 1] == 0x14 && module[i + 2] == 0xd0) {
|
|
if (warnings++ > 0)
|
|
printf(", ");
|
|
printf("WARNING: possible PAL/NTSC detection code");
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASAPInfo_Delete(info);
|
|
if (warnings == 0)
|
|
printf("ok");
|
|
printf("\n");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
bool usage = true;
|
|
for (int i = 1; i < argc; i++) {
|
|
const char *arg = argv[i];
|
|
if (strcmp(arg, "--help") == 0) {
|
|
usage = true;
|
|
break;
|
|
}
|
|
if (strcmp(arg, "--version") == 0) {
|
|
printf("sap2ntsc " ASAPInfo_VERSION "\n");
|
|
return 0;
|
|
}
|
|
process_file(arg);
|
|
usage = false;
|
|
}
|
|
if (usage) {
|
|
printf(
|
|
"Usage: sap2ntsc FILE.sap...\n"
|
|
"Replaces FILE.sap with an NTSC version\n"
|
|
);
|
|
}
|
|
return 0;
|
|
}
|