
/*************************************************
****            Locale library V1.1           ****
****       Language localization library      ****
**** Copyright 2000-2002, Alessandro Pedretti ****
**************************************************/

/*
  The following routines use the AmigaOS Locale Technology to translate the
  strings in any language using a specific catalog file in standard IFF format.
  Each locale file must be stored in a sub-directory with the lower-case language
  name of the Locale directory.
  The AmigaOS Locale Technology is copyrighted by Amiga Inc.
*/

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <malloc.h>

#include "iff.h"
#include "locale.h"
#include "tables.h"

#ifdef WIN32
#  include <windows.h>
#endif

/**** Definitions ****/

typedef struct {
  void          *Begin;
  unsigned int  Size;
} LOC_INFO;

/**** Global variables ****/

static int              LocGlobalErr;
extern int              errno;

/**** Local variables ****/

static int              LocMinVer;

/**** Local prototypes ****/

static FILE *OpenIff(char *, unsigned int);
static short ReadIffHdr(FILE *, IFFHDR *, unsigned int);
static void  Swap(void *);


/**** Decode a standard AmigaOS version string ****/

LOCEXTERN void LOCEXPORT(LocAmigaVer)(register char *Str, AMIGAVER *Ver)
{
  *Ver -> Name = 0;
  *Ver -> Date = 0;
  Ver -> Ver   = 0;
  Ver -> Rel   = 0;

  if (!strncmp(Str, "$VER", 4)) {
    Str += 5;
    while((*Str) && (*Str == ' ')) ++Str;
    sscanf(Str, "%31s %d.%d (%13s ", Ver -> Name, &Ver -> Ver,
           &Ver -> Rel, Ver -> Date);
    Ver -> Date[strlen(Ver -> Date) - 1] = 0;
  }
}


/**** Close the catalog ****/

LOCEXTERN void LOCEXPORT(LocCloseCat)(void *LocInfo)
{
  if (LocInfo){
    free(((LOC_INFO *)LocInfo) -> Begin);
    free(LocInfo);
  }
  LocGlobalErr = LOCERR_NONE;
}


/**** fprintf replacement with iso2dos character conversion ****/

LOCEXTERN int LOCEXPORT(LocDosPrintf)(FILE *FH, char *Str, ...)
{
  char          Buf[1024];
  va_list       vl;
  int			Ret;

  va_start(vl, Str);
  vsprintf(Buf, Str, vl);
  LocGlobalErr = LOCERR_NONE;
  LocIso2Dos(Buf, FALSE);
  Ret = fprintf(FH, Buf);
  va_end(vl);

  return Ret;
}


/**** Convert the ISO-Latin-1 character set into MS-DOS format ****/

LOCEXTERN void LOCEXPORT(LocIso2Dos)(register char *Str, short Back)
{
  register KNOWN_PAIRS     *Pair;

  while(*Str) {
    if ((unsigned char)*Str > (unsigned char)127) {
      for(Pair = KnownPairs; Pair -> Dos; ++Pair) {
        if (Back) {
          if (*Str == Pair -> Dos) {
            *Str = Pair -> Iso;
            break;
          }
        } else {
          if (*Str == Pair -> Iso) {
            *Str = Pair -> Dos;
            break;
          }
       }
      } /* End of pair loop */
    }
    ++Str;
  } /* End of string loop */

  LocGlobalErr = LOCERR_NONE;
}


/**** Translate a catalog string                 ****/
/**** compatible with AmigaOS GetCatalogString() ****/

LOCEXTERN char * LOCEXPORT(LocGetCatStr)(void *LocInfo, register unsigned int Id, char *BuiltIn)
{
  register unsigned int        *Ptr, Len;
  register unsigned int        CatEnd = (unsigned int)((LOC_INFO *)LocInfo) -> Begin + ((LOC_INFO *)LocInfo) -> Size;

  Ptr = (unsigned int *)((LOC_INFO *)LocInfo) -> Begin;
  while((unsigned int)Ptr < CatEnd) {
    if (Id == *Ptr) {
      BuiltIn = (char *)Ptr + 8;
      break;
    }
    Len = Ptr[1];
    if (Len & 3) Len += 4 - (Len & 3);
    (char *)Ptr += Len + 8;
  }
  LocGlobalErr = LOCERR_NONE;

  return BuiltIn;
}


/****       Open a catalog file      ****/
/**** (only for non-AmigaOS systems) ****/

LOCEXTERN void * LOCEXPORT(LocOpenCat)(char *CatalogName, char *Path, char *Language,
                  unsigned int MinVer, char *BuiltIn)
{
  AMIGAVER       Ver;
  char           CatalogPath[512];
  FILE           *FH;
  IFFHDR         Hdr;
#ifdef LITTLE_ENDIAN
  unsigned int   *Ptr, Len;
#endif

  LOC_INFO       *LocInfo = NULL;
  short          Ret      = TRUE;
  unsigned short Chk      = 0;

  if (strcasecmp(Language, BuiltIn)) {

    /**** Set the path and file name ****/

    strcpy(CatalogPath, Path);
#ifdef WIN32
    if (Path[strlen(Path) - 1] != '\\')
#else
    if (Path[strlen(Path) - 1] != '/')
#endif
      strcat(CatalogPath, C_DIRSEP);
    strcat(CatalogPath, LOCALE_DIR C_DIRSEP);
/*
    MessageBox(NULL, CatalogPath, "Locale", MB_OK);
*/
    if (!strcmp(Language, "auto")) {
#ifdef WIN32
      if ((Language = LocGetLangStr()) == NULL) {
        LocGlobalErr = LOCERR_AUTOWIN;
        return NULL;
      }
#else
      LocGlobalErr = LOCERR_AUTONOTSUP;
      return NULL;
#endif
    }
    strcat(CatalogPath, Language);
    strcat(CatalogPath, C_DIRSEP);
    strcat(CatalogPath, CatalogName);

    /**** Read the IFF file ****/

    if ((FH = OpenIff(CatalogPath, IFF_CTLG)) != NULL) {
      while((Ret) && (ReadIffHdr(FH, &Hdr, 0))) {
        switch(Hdr.ID) {

          case IFF_CTLG_FVER:  /* Version chunk */
            if ((Hdr.Size <= sizeof(CatalogPath)) &&
                (fread(CatalogPath, Hdr.Size, 1, FH) == 1)) {
              LocAmigaVer(CatalogPath, &Ver);
              if (!strcasecmp(CatalogName, Ver.Name)) {
                if (Ver.Ver < MinVer) {
                  LocGlobalErr = LOCERR_VERSION;
                  LocMinVer    = MinVer;
                  Ret          = FALSE;
                } else ++Chk;
              } else {
                LocGlobalErr = LOCERR_ANOTHERAPP;
                Ret = FALSE;
              }
            } else {
              LocGlobalErr = LOCERR_CORRUPT;
              Ret          = FALSE;
            }
            break;

          case IFF_CTLG_LANG:  /* Language chunk */
            if ((Hdr.Size <= sizeof(CatalogPath)) &&
                (fread(CatalogPath, Hdr.Size, 1, FH) == 1)) {
              if (strcasecmp(CatalogPath, Language)) {
                LocGlobalErr = LOCERR_LANGUAGE;
                Ret          = FALSE;
              } else ++Chk;
            } else {
              LocGlobalErr = LOCERR_CORRUPT;
              Ret          = FALSE;
            }
            break;

          case IFF_CTLG_STRS:  /* String chunk */
            if (((LocInfo = (LOC_INFO *)malloc(sizeof(LOC_INFO))) != NULL) &&
                ((LocInfo -> Begin = (void *)malloc(Hdr.Size)) != NULL)) {
              if (fread(LocInfo -> Begin, Hdr.Size, 1, FH) == 1) {
                LocInfo -> Size = Hdr.Size;

                /**** Set the endian (if needed) ****/

#ifdef LITTLE_ENDIAN
                Ptr = (unsigned int *)LocInfo -> Begin;
                while((unsigned int)Ptr < ((unsigned int)LocInfo -> Begin + Hdr.Size)) {
                  Swap(Ptr);
                  Swap(Ptr + 1);
                  Len = Ptr[1];
                  if (Len & 3) Len += 4 - (Len & 3);
                  (char *)Ptr += 8 + Len;
                }
#endif
                ++Chk;
              } else {
                LocGlobalErr = LOCERR_CORRUPT;
                Ret          = FALSE;
                free(LocInfo -> Begin);
                free(LocInfo);
                LocInfo = NULL;
              }
            } else LocGlobalErr = LOCERR_MEMORY;
            break;

          default:            /* Skip the unknown chunk */
            fseek(FH, Hdr.Size, SEEK_CUR);
        }
      }
      if ((Chk != 3) && (LocInfo)) {
        free(LocInfo -> Begin);
        free(LocInfo);
        LocInfo = NULL;
      }
      fclose(FH);
    } else LocGlobalErr = LOCERR_NOTFOUND;
  } else LocGlobalErr = LOCERR_NONE;

  return (void *)LocInfo;
}


/**** Copy a string ISO-Latin-1 into another MS-DOS string ****/

LOCEXTERN void LOCEXPORT(LocStrCpy)(register char *Dest, register char *Src)
{
  register KNOWN_PAIRS     *Pair;

  while(*Src) {
    if ((unsigned char)*Src > (unsigned char)127) {
      for(Pair = KnownPairs; Pair -> Dos; ++Pair) {
        if (*Src == Pair -> Iso) {
            *Dest = Pair -> Dos;
            break;
        }
      } /* End of pair loop */
      if (!Pair -> Dos) *Dest = *Src;
    } else *Dest = *Src;
    ++Src;
    ++Dest;
  } /* End of string loop */
  *Dest = 0;

  LocGlobalErr = LOCERR_NONE;
}


/**** Open an IFF file for read ****/
/**** and eventually check it   ****/

FILE *OpenIff(char *Name, unsigned int Comp)
{
  FILE           *FH;
  IFFHDR         Hdr;

  LocGlobalErr = LOCERR_NONE;

  if ((FH = fopen(Name, "rb")) != NULL) {
    if (ReadIffHdr(FH, &Hdr, IFF_FORM)) {
      if (fread(&Hdr, sizeof(unsigned int), 1, FH) == 1) {

#ifdef LITTLE_ENDIAN
        Swap(&Hdr.ID);
#endif

        if (Comp != Hdr.ID) {
          LocGlobalErr = LOCERR_CORRUPT;
          fclose(FH);
          FH = NULL;
        }
      } else {
        LocGlobalErr = LOCERR_DOS;
        fclose(FH);
        FH = NULL;
      }
    }
  }

  return FH;
}


/**** Specific Win32 soubroutines ****/

#ifdef WIN32

/**** Get the default system language ****/

LOCEXTERN char * LOCEXPORT(LocGetLangStr)(void)
{
  LANGID                Lang;
  register LANGTAB      *k;

  if ((Lang = (LANGID)(GetSystemDefaultLangID() & 0x3ff)) != 0) {
    for(k = (LANGTAB *)LangTab; (k -> ID) && (k -> ID != Lang); ++k);
    if (k) {
      LocGlobalErr = LOCERR_NONE;
      return k -> LangStr;
    }
  }
  LocGlobalErr = LOCERR_LANGSTR;

  return NULL;
}
#endif

/**** Read an IFF header and eventually check it with ****/
/**** the Comp value                                  ****/

short ReadIffHdr(FILE *FH, IFFHDR *Hdr, unsigned int Comp)
{
  short          Ret = FALSE;

  /**** Strip the padding byte ****/

  if (!feof(FH)) {
    if (ftell(FH) & 1) fgetc(FH);
    if (fread(Hdr, sizeof(IFFHDR), 1, FH) == 1) {

#ifdef LITTLE_ENDIAN
      Swap(&Hdr -> ID);
      Swap(&Hdr -> Size);
#endif
      if ((!Comp) || (Comp == Hdr -> ID)) Ret = TRUE;
    }
  }

  return Ret;
}

/**** Change the endian ****/

#ifdef LITTLE_ENDIAN
void Swap(register void *Val)
{
  register char    T;

  T                = ((char *)Val)[0];
  ((char *)Val)[0] = ((char *)Val)[3];
  ((char *)Val)[3] = T;
  T                = ((char *)Val)[1];
  ((char *)Val)[1] = ((char *)Val)[2];
  ((char *)Val)[2] = T;
}
#endif


/**** Get the error code ****/

LOCEXTERN int LOCEXPORT(LocGetErr)(void)
{
  return LocGlobalErr;
}


/**** Get the error message ****/

LOCEXTERN void LOCEXPORT(LocGetErrStr)(char *Buf, int Size)
{
  char          *Str, MyBuf[256];

  Str = MyBuf;
  --Size;

  switch(LocGlobalErr) {
  case LOCERR_NONE:
    Str = "None";
    break;

  case LOCERR_DOS:
    Str = strerror(errno);
    break;

  case LOCERR_CORRUPT:
    Str = "Corrupted catalog file";
    break;

  case LOCERR_VERSION:
    sprintf(MyBuf, "The catalog version %d.0 or better is required", LocMinVer);
    break;

  case LOCERR_MEMORY:
    Str = "Out of memory";
    break;

  case LOCERR_ANOTHERAPP:
    Str = "Catalog of another application";
    break;

  case LOCERR_LANGUAGE:
    Str = "The catalog has a wrong language";
    break;

  case LOCERR_AUTONOTSUP:
    Str = "The automatic language detection isn't supported";
    break;

  case LOCERR_AUTOWIN:
    Str = "Can't detect the default language";
    break;

  case LOCERR_NOTFOUND:
    Str = "Catalog file not found";
    break;

  default:
    Str = "Unknown";
  }
  strncpy(Buf, Str, Size);
}


