                          /**************************************************/
                          /*                                                */
                          /*                   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   =   160;
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},
    {"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 != NULL)
    {
        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);
}
///


void wbmain(WBStartup *wbStartup)
{
    CStartup *startup;

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

    // Get default settings from the ToolTypes.

    if (startup != NULL)
    {
        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)
{
    switch (errorCode)
    {
        case CStartup::NO_MEMORY:
            display->PrintDOSError(ERROR_NO_FREE_STORE);
            break;

        case CStartup::READARGS_ERROR:
            display->PrintDOSError(DOSLib::IoErr());
            break;

        case CStartup::BAD_ARGUMENT:
            display->PrintErrorMsg("Illegal argument");
            break;

        default:
            break;
    }
}


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

int ShellEncoder()
{
    // Pas de fichier source : ouvrir un requester.

    if (GlobalConfig.CurrentIndex() == 0)
    {
        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() == 0)
        {
            display->PrintDOSError(ERROR_REQUIRED_ARG_MISSING);
            return RETURN_FAIL;
        }
    }

    // Collationer toute la liste des fichiers audio.

    if (CollectFiles() != NO_ERROR || GlobalConfig.CurrentIndex() == 0)
    {
        display->PrintDOSError(ERROR_OBJECT_NOT_FOUND);
        return RETURN_FAIL;
    }

    // Traiter les fichiers audio indiqués (mode SHELL).

    display->DisplayBanner();                               // Afficher le Copyright.
    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) == FALSE) 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;
}
///

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.SetDestination(goPegaseArgs.pa_To);
    GlobalConfig.SetPattern(goPegaseArgs.pa_Pattern);

    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

    ecfg_wSampling       = 44100;                // Fréquence d'échantillonage = 44,1 KHz
    ecfg_sModes.sampfreq = F_44100;              // Code de fréquence
    ecfg_wBitrate        = 128;                  // Taux de sortie = 128 kbits/s
    ecfg_sModes.bitrate  = 7;                    // Index du taux de sortie.
}
///
/// 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()) != NULL)
        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 == NULL || *cDest == 0)
    {
        t = TARGET_EMPTY;
    }
    else
    {
        if ( (lock.Lock(cDest, CLock::READ)) )
        {
            if ( (pFIB = (FileInfoBlock *) obj.Allocate(CDOSObject::FIB)) )
            {
                if (::Examine(lock, pFIB))
                {
                    if (pFIB->fib_DirEntryType >= 0)
                    {
                        t = TARGET_DIRECTORY;
                    }
                    else
                    {
                        t = TARGET_FILE;
                    }
                }
            }
        }
    }

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

