                          /**************************************************/
                          /*                                                */
                          /*                   Pegase.cpp                   */
                          /*                                                */
                          /**************************************************/

/*
** $Revision: 1.3 $
** $State: Exp $
** $Date: 1998/08/16 19:03:34 $
**
** $Log: Pegase.cpp $
** Revision 1.3  1998/08/16 19:03:34  kakace
** Version Beta3+
**
*/


//----------------------------------------------------------------------------------------------------

/// Includes

#ifndef  _PEGASECOND_HPP
#include "PegaseCond.hpp"
#endif

#ifndef  _FILEINFOS_CLASS_HPP
#include "FileInfosC.hpp"
#endif

#ifndef  _PEGASE_HPP
#include "Pegase.hpp"
#endif

#ifndef  CLASS_DISPLAY_HPP
#include "Display.hpp"
#endif

#ifndef  _COMMON_HPP
#include "Common.hpp"
#endif

#ifndef  _ARGSCONTROL_CLASS_HPP
#include "ArgsControlClass.hpp"
#endif

///

#define CATCOMP_NUMBERS
#ifndef  PEGASE_H
#include "Pegase.h"
#endif


//----------------------------------------------------------------------------------------------------

extern const char *_verstr_;

PegaseConfigC GlobalConfig;         // Paramètres par défaut de l'encodeur.
Display *display;


// Resource tracking.
//  Initialise le pointeur "display" avant d'appeler main() ou wbmain() et libère les ressources
//  associées en fin de programme.

static struct DispResource {
    DispResource() { ::display = NULL; }
    ~DispResource() { delete ::display; }
} dispResource;


//----------------------------------------------------------------------------------------------------
//============================================ Arguments =============================================
//----------------------------------------------------------------------------------------------------

// Parameters storage

CPegaseArgs goPegaseArgs;


/// CArgsControl goPegaseArgsControl[]

#define GET_OFFSET(a)   (offsetof(CPegaseArgs, a) / sizeof(LONG))


// Parameters definition

const LONG default_layer     =     2;
const LONG default_frequency = 44100;
const LONG default_bitrate   =   DEFAULT_BITRATE;
const LONG default_priority  =     0;
const LONG default_FRXPos    =    20;
const LONG default_FRYPos    =    20;
const LONG default_FRWidth   =   300;
const LONG default_FRHeight  =   220;

const CArgsControl goPegaseArgsControl[] = {
    {"FROM",      "/M",      GET_OFFSET(pa_From),      ARG_STRING_ARRAY, ARGF_CLI, 0, 0, 0 },
    {"TO",        "/K",      GET_OFFSET(pa_To),        ARG_STRING,       ARGF_CLI | ARGF_WB, 0, 0, 0},
    {"LAYER",     "/K/N",    GET_OFFSET(pa_Layer),     ARG_NUMBER,       ARGF_CLI_WB_DEF_CHECK, (LONG) &default_layer, 1, 2},
    {"FREQ",      "/K/N",    GET_OFFSET(pa_Frequency), ARG_NUMBER,       ARGF_CLI_WB_DEF_CHECK, (LONG) &default_frequency, 30769, 49920},
    {"BITRATE",   "=BR/K/N", GET_OFFSET(pa_Bitrate),   ARG_NUMBER,       ARGF_CLI_WB_DEF_CHECK, (LONG) &default_bitrate, 32, 448},
    {"MONO",      "/S",      GET_OFFSET(pa_Mono),      ARG_BOOL,         ARGF_CLI_WB_DEF, 0, 0, 0},
    {"JSTEREO",   "/S",      GET_OFFSET(pa_JStereo),   ARG_BOOL,         ARGF_CLI_WB_DEF, 0, 0, 0},
    {"COPYRIGHT", "/S",      GET_OFFSET(pa_Copyright), ARG_BOOL,         ARGF_CLI_WB_DEF, 0, 0, 0},
    {"ORIGINAL",  "/S",      GET_OFFSET(pa_Original),  ARG_BOOL,         ARGF_CLI_WB_DEF, 0, 0, 0},
    {"CRC",       "/S",      GET_OFFSET(pa_CRC),       ARG_BOOL,         ARGF_CLI_WB_DEF, 0, 0, 0},
    {"PRIORITY",  "/K/N",    GET_OFFSET(pa_Priority),  ARG_NUMBER,       ARGF_CLI_WB_DEF_CHECK, (LONG) &default_priority, -128, 5},
    {"VERBOSE",   "/S",      GET_OFFSET(pa_Verbose),   ARG_BOOL,         ARGF_CLI_WB_DEF, 0, 0, 0},
    {"INTEL",     "/S",      GET_OFFSET(pa_UseIntFmt), ARG_BOOL,         ARGF_CLI, 0, 0, 0},
    {"MOTOROLA",  "/S",      GET_OFFSET(pa_UseMotFmt), ARG_BOOL,         ARGF_CLI, 0, 0, 0},
    {"DELETE",    "/S",      GET_OFFSET(pa_Delete),    ARG_BOOL,         ARGF_CLI_WB_DEF, 0, 0, 0},
    {"PATTERN",   NULL,      GET_OFFSET(pa_Pattern),   ARG_STRING,       ARGF_WB_DEF, (LONG) "~(#?.info)", 0, 0},

    {"FR_SOURCE", NULL,      GET_OFFSET(pa_DefSource), ARG_STRING,       ARGF_WB, 0, 0, 0},
    {"FR_XPOS",   NULL,      GET_OFFSET(pa_FR_XPos),   ARG_NUMBER,       ARGF_WB_DEF, (LONG) &default_FRXPos, 0, 0},
    {"FR_YPOS",   NULL,      GET_OFFSET(pa_FR_YPos),   ARG_NUMBER,       ARGF_WB_DEF, (LONG) &default_FRYPos, 0, 0},
    {"FR_WIDTH",  NULL,      GET_OFFSET(pa_FR_Width),  ARG_NUMBER,       ARGF_WB_DEF, (LONG) &default_FRWidth, 0, 0},
    {"FR_HEIGHT", NULL,      GET_OFFSET(pa_FR_Height), ARG_NUMBER,       ARGF_WB_DEF, (LONG) &default_FRHeight, 0, 0},
    {NULL}
};
///

//----------------------------------------------------------------------------------------------------
//======================================== Prototypes privés =========================================
//----------------------------------------------------------------------------------------------------

e_FileListErrors CollectFiles();
void    ShowEncodingInfos(const EncoderConfigC &roConfig);
void    EncodeFiles();
void    InitConfig();
int     ShellEncoder();
void    ShowStartupError(CStartup::Errors errorCode);


//----------------------------------------------------------------------------------------------------
//===================================== Modules d'initialisation =====================================
//----------------------------------------------------------------------------------------------------

/// CheckCPUModel()
/****** CheckCPUModel/ ******************************************************
*
*   NAME
*   CheckCPUModel -- Contrôler le type de CPU et la version de l'OS
*
*   SYNOPSIS
*   CheckCPUModel()
*
*   void CheckCPUModel();
*
*   FUNCTION
*   Contrôle que le CPU utilisé est compatible avec la version du programme,
*   et vérifie que la version de l'OS est suffisante.
*   Dans le cas contraire, une erreur est affichée, et le programme est
*   avorté.
*
*   NOTES
*   Le niveau de cette fonction INIT assure que le fichier ".catalog" est
*   déjà chargé. De fait, le message d'erreur peut être localisé.
*
*****************************************************************************
*
*/

#ifndef  EXEC_EXECBASE_H
#include <exec/execbase.h>
#endif

void CheckCPUModel()
{
#ifndef __PPC__

    extern struct ExecBase *SysBase;

#if   _CPUMODEL_ == 68020
    const UWORD CPUMASK = AFF_68020;                        // = 68020/68030 sans FPU
#elif _CPUMODEL_ == 68030
    const UWORD CPUMASK = AFF_68020|AFF_68881;              // = 68020/68030 + 68881/68882
#elif _CPUMODEL_ == 68040
    const UWORD CPUMASK = AFF_68040|AFF_FPU40|AFF_68881;    // = 68040+FPU
#elif _CPUMODEL_ == 68060
    const UWORD CPUMASK = 0x80|AFF_FPU40|AFF_68881;         // = 68060+FPU
#endif


    if (SysBase->LibNode.lib_Version < 37)
        exit(20);        // Version OS incorrecte.

    if ((SysBase->AttnFlags & CPUMASK) != CPUMASK) {
        EasyStruct sBadCPU = {sizeof(EasyStruct), 0, };

        sBadCPU.es_Title        = (char *) GetString(MSG_FATALERROR_TXT);
        sBadCPU.es_TextFormat   = (char *) GetString(MSG_BADCPU_TXT);
        sBadCPU.es_GadgetFormat = (char *) GetString(MSG_QUIT_GADGET);

        // Afficher un requester d'erreur si la version du programme ne correspond pas
        // avec le modèle de CPU utilisé.

        IntuitionLib::EasyRequestArgs(NULL, &sBadCPU, NULL, NULL);

        exit(20);
    }

#endif  // __PPC__
}
///


//----------------------------------------------------------------------------------------------------
//====================================== Fonctions de démarrage ======================================
//----------------------------------------------------------------------------------------------------


/// main()
//----------------------------------------------------------------------------------------------------
// NOTE : L'ordre logique des choses, en ce qui concerne les paramètres de l'encodeur, est
//        le suivant :
//     · CLI : - Initialisation de PegaseConfigC avec les valeurs par défaut.
//             - Lecture des ToolTypes => Initialisation de s_Def_params.
//             - Analyse de la ligne de commande => Modification de s_Def_params.
//             - Mise à jour de PegaseConfigC en fonction des choix de l'utilisateur.
//     · WB :  - Initialisation de PegaseConfigC avec les valeurs par défaut.
//             - Lecture des ToolTypes => Initialisation de s_Def_params.
//             - Mise à jour de PegaseConfigC en fonction des données définies par les
//               ToolTypes.
//
//   Si l'option NOGUI est spécifiée, la configuration de l'encodeur est
//   affichée à l'écran. De même, si aucune source n'est mentionnée, un
//   requester de fichier est ouvert de façon à laisser une dernière chance à
//   l'utilisateur pour choisir les fichiers à encoder.


void main()
{
    CStartup *startup;
    int return_code = RETURN_FAIL;

    CheckCPUModel();
    display = new CLIDisplay;
    startup = new CStartup(goPegaseArgsControl, (LONG *) &goPegaseArgs, GetString(MSG_EXTENDED_HELP));

    if (startup) {
        CStartup::Errors startup_error;

        if ( (startup_error = startup->ParseArgs()) == CStartup::NO_ERROR) {
            InitConfig();
            return_code = ShellEncoder();
        }
        else
            ShowStartupError(startup_error);

        delete startup;
    }
    else
        display->PrintDOSError(ERROR_NO_FREE_STORE);

    exit(return_code);
}
///
/// wbmain()
void wbmain(WBStartup *wbStartup)
{
    CStartup *startup;

    CheckCPUModel();
    display = new WBDisplay;
    startup = new CStartup(goPegaseArgsControl, (LONG *) &goPegaseArgs);

    // Get default settings from the ToolTypes.

    if (startup) {
        CStartup::Errors startup_error;

        if ( (startup_error = startup->ParseToolTypes(wbStartup->sm_ArgList->wa_Name)) == CStartup::NO_ERROR) {
            InitConfig();
            ShellEncoder();
        }
        else
            ShowStartupError(startup_error);

        delete startup;
    }
    else
        display->PrintDOSError(ERROR_NO_FREE_STORE);
}


void ShowStartupError(CStartup::Errors errorCode)
{
    if (errorCode == CStartup::NO_MEMORY)
        display->PrintDOSError(ERROR_NO_FREE_STORE);
    else if (errorCode == CStartup::READARGS_ERROR)
        display->PrintDOSError(DOSLib::IoErr());
    else if (errorCode == CStartup::BAD_ARGUMENT)
        display->PrintErrorMsg(GetString(MSG_ILLEGAL_ARGUMENT_ERROR));
}
///

/// ShellEncoder()
//----------------------------------------------------------------------------------------------------
//========================================== ShellEncoder() ==========================================
//----------------------------------------------------------------------------------------------------

int ShellEncoder()
{
    // Pas de fichier source : ouvrir un requester.
    if (!GlobalConfig.CurrentIndex()) {
        RequestFileList();

        // Avorter si aucun fichier n'est sélectionné (on ne peut rien faire d'autre si
        // l'utilisateur a volontairement désactivé l'interface...)
        if (!GlobalConfig.CurrentIndex()) {
            display->PrintDOSError(ERROR_REQUIRED_ARG_MISSING);
            return RETURN_FAIL;
        }
    }

    display->DisplayBanner();                               // Afficher le Copyright.

    // Collationer toute la liste des fichiers audio.
    if (CollectFiles() != NO_ERROR || !GlobalConfig.CurrentIndex()) {
        display->PrintDOSError(ERROR_OBJECT_NOT_FOUND);
        return RETURN_FAIL;
    }

    // Traiter les fichiers audio indiqués (mode SHELL).
    display->ShowConfig(GlobalConfig);                      // Afficher les réglages retenus par l'encodeur.

    // Encoder tous les fichiers audio trouvés.
    FileInfosC *poFileInfos;

    for (poFileInfos = (FileInfosC *) GlobalConfig.First(); poFileInfos; poFileInfos = (FileInfosC *) poFileInfos->Next()) {
        poFileInfos->DefineOutputName();
        display->ShowCurrentJob(poFileInfos);
        display->ShowEncodingInfos(poFileInfos);

        if (!EncodeFile(*poFileInfos))
            break;       // Arrêt global (mode batch)
    }

    // Restaurer la priorité du Shell.
    GlobalConfig.ChangePriority();

    return RETURN_OK;
}
///
/// CollectFiles()
//----------------------------------------------------------------------------------------------------
//========================================== CollectFiles() ==========================================
//----------------------------------------------------------------------------------------------------
/****** Class CollectFiles/ *************************************************
*
*   NAME
*   CollectFiles -- Collationner la liste des fichiers à encoder.
*
*   SYNOPSIS
*   Erreur = CollectFiles()
*
*   e_FileListErrors CollectFiles();
*
*   FUNCTION
*   Recherche tous les fichiers audio à encoder en partant des informations
*   fournies par l'utilisateur. Chaque fichier trouvé est examiné afin de
*   déterminer son type, ainsi que les données qui le caractérisent.
*
*   RESULT
*   Erreur - Code d'erreur.
*
*   NOTES
*   La fonction avorte immédiatement si un manque de mémoire est détecté.
*
*****************************************************************************
*
*/


e_FileListErrors CollectFiles()
{
    ULONG iFileIndex = 0;
    e_FileListErrors iListError = NO_ERROR;

    // Ajuster la destination en fonction des conditions.

    if (GlobalConfig.CurrentIndex() > 1 && GlobalConfig.GetTargetType() == TARGET_FILE) {
        GlobalConfig.SetDestination(NULL);
        GlobalConfig.SetTargetType(TARGET_EMPTY);
    }

    // Obtenir la liste de tous les fichiers à encoder. La boucle se charge de faire examiner chacune
    // des sources indiquée sur la ligne de commande (ou via le requester de fichier), que ces sources
    // soient des fichiers ou des répertoires.
    for (iFileIndex = 0; iFileIndex < GlobalConfig.CurrentIndex(); iFileIndex++) {
        if ( (iListError = ReadFileList(GlobalConfig, iFileIndex) ) != NO_ERROR) {
            // Avorter en cas d'erreur.
            return iListError;
        }
    }
    return NO_ERROR;
}
///
/// InitConfig()
void InitConfig()
{
    // Modifier la priorité de fonctionnement si nécessaire.
    if (*goPegaseArgs.pa_Priority)
        GlobalConfig.ChangePriority(*goPegaseArgs.pa_Priority);

    // Type de Layer à utiliser pour l'encodage.
    GlobalConfig.SetLayer(*goPegaseArgs.pa_Layer);

    // Mode de fonctionnement de l'encodeur (mode stéréo si aucun de ces arguments n'est indiqué).
    // (RDArgs retourne la valeur -1 lorsqu'un argument est indiqué)
    if (goPegaseArgs.pa_Mono + goPegaseArgs.pa_JStereo < -1)
        goPegaseArgs.pa_Mono = goPegaseArgs.pa_JStereo = 0;


    if (goPegaseArgs.pa_Mono)           GlobalConfig.SetEncodingMode(MONO);
    else if (goPegaseArgs.pa_JStereo)   GlobalConfig.SetEncodingMode(JOINTSTEREO);
    else                                GlobalConfig.SetEncodingMode(STEREO);          // Mode par défaut.

    // Drapeaux divers.
    if (goPegaseArgs.pa_Copyright)     GlobalConfig.SetCopyrightBit(TRUE);
    if (goPegaseArgs.pa_Original)      GlobalConfig.SetOriginalBit(TRUE);
    if (goPegaseArgs.pa_CRC)           GlobalConfig.SetCRCBit(TRUE);

    if (goPegaseArgs.pa_UseIntFmt != goPegaseArgs.pa_UseMotFmt)
        GlobalConfig.SetCDDAFmt(goPegaseArgs.pa_UseMotFmt ? 1 : 2);

    // Fréquence d'échantillonage. On sélectionne la fréquence standard la plus proche de celle
    // mentionnée par l'utilisateur.
    if (!GlobalConfig.SetSampleFreq(*goPegaseArgs.pa_Frequency))
        GlobalConfig.SetSampleFreq(44100);

    // Taux de sortie de l'encodeur.

    GlobalConfig.SetBitRate(*goPegaseArgs.pa_Bitrate);
    GlobalConfig.SetVerboseBit(goPegaseArgs.pa_Verbose);
    GlobalConfig.DeleteSource(goPegaseArgs.pa_Delete);
    GlobalConfig.SetPattern(goPegaseArgs.pa_Pattern);
    GlobalConfig.SetDestination(goPegaseArgs.pa_To);

    if (goPegaseArgs.pa_From)
        GlobalConfig = goPegaseArgs.pa_From;
}
///

//----------------------------------------------------------------------------------------------------
//======================================= Classe PegaseConfigC =======================================
//----------------------------------------------------------------------------------------------------

/// PegaseConfigC::CONSTRUCTOR
/****** Class PegaseConfigC/CONSTRUCTOR *************************************
*
*   NAME
*   PegaseConfigC::PegaseConfigC -- Constructeur
*
*   SYNOPSIS
*   PegaseConfigC::PegaseConfigC()
*
*   PegaseConfigC::PegaseConfigC();
*
*   FUNCTION
*   Initialise les paramètres par défaut de l'encodeur.
*
*****************************************************************************
*
*/


PegaseConfigC::PegaseConfigC()
{
    cfg_cPattern         = NULL;
    ecfg_iLayer          = LAYER_II;             // Encodage en Layer II
    ecfg_sFlags.encoding = STEREO;               // Mode stéréo
    ecfg_sFlags.psycho2  = TRUE;                 // Modèle psycho-acoustique n°2
    SetBitRate();
    SetSampleFreq(44100);
}
///
/// PegaseConfigC::DESTRUCTOR
/****** Class PegaseConfigC/DESTRUCTOR **************************************
*
*   NAME
*   PegaseConfigC::~PegaseConfigC -- Destructeur
*
*   SYNOPSIS
*   PegaseConfigC::~PegaseConfigC()
*
*   PegaseConfigC::~PegaseConfigC();
*
*   FUNCTION
*   Libère chaque objet FileInfosC contenu dans la liste avant de détruire
*   la liste.
*
*****************************************************************************
*
*/


PegaseConfigC::~PegaseConfigC()
{
    class FileInfosC *poNode;

    while ( (poNode = (FileInfosC *) RemHead()) )
        delete poNode;

    if (cfg_cPattern)
        delete cfg_cPattern;
}
///
/// PegaseConfigC::SetDestination()
void PegaseConfigC::SetDestination(STRING cDest)
{
    FileInfoBlock *pFIB;
    CDOSObject     obj;
    CLock          lock;
    e_Targets t = TARGET_FILE;

    if ( !cDest || !(*cDest))
        t = TARGET_EMPTY;
    else {
        if ( (lock.Lock(cDest, CLock::READ)) ) {
            if ( (pFIB = (FileInfoBlock *) obj.Allocate(CDOSObject::FIB)) ) {
                if (::Examine(lock, pFIB))
                    t = (pFIB->fib_DirEntryType >= 0) ? TARGET_DIRECTORY : TARGET_FILE;
            }
        }
    }

    SetOutputName((char *) cDest);
    SetTargetType(t);
}
///

