Logo Search packages:      
Sourcecode: easyh10 version File versions  Download package

easyh10_tuner.c

/*
 *      EasyH10 tuner routine.
 *
 *      Copyright (c) 2005 Nyaochi
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
 * http://www.gnu.org/copyleft/gpl.html .
 *
 */

/* $Id: easyh10_tuner.c,v 1.7 2006/05/05 09:34:20 nyaochi Exp $ */

#ifdef      HAVE_CONFIG_H
#include <config.h>
#endif/*HAVE_CONFIG_H*/

#include <os.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucs2char.h>

#include <easyh10.h>
#include <filepathutil.h>

#define     MAX_STATIONS      20
#define     MAX_STATIONS2     (MAX_STATIONS * 2)

struct tag_tuner_entry {
      ucs2_char_t name[11];
      uint32_t frequency;
};
typedef struct tag_tuner_entry tuner_entry_t;

struct tag_tuner_dat {
      uint8_t     unknown1;
      uint8_t unknown2;
      uint8_t     region;
      uint8_t     num_entries;
      uint8_t     unknown3;
      tuner_entry_t stations[MAX_STATIONS2];
};
typedef struct tag_tuner_dat tuner_dat_t;

static const ucs2_char_t ucs2cs_tuner_dat[] = {'T','u','n','e','r','.','d','a','t',0};



static int serialize_uint8(FILE *fp, uint8_t* val, int is_storing)
{
      if (is_storing) {
            return (fwrite(val, sizeof(uint8_t), 1, fp) == 1) ? 0 : 1;
      } else {
            return (fread(val, sizeof(uint8_t), 1, fp) == 1) ? 0 : 1;
      }
}

static int serialize_ucs2le_char(FILE *fp, ucs2_char_t* val, int is_storing)
{
      int ret;
      uint8_t v[2];

      if (is_storing) {
            v[0] = (uint8_t)(*val & 0xFF);
            v[1] = (uint8_t)(*val >> 8);
            ret = (fwrite(v, sizeof(uint8_t), 2, fp) == 2) ? 0 : 1;
      } else {
            ret = (fread(v, sizeof(uint8_t), 2, fp) == 2) ? 0 : 1;
            *val = (ucs2_char_t)v[1] << 8 | (ucs2_char_t)v[0];
      }
      return ret;
}

static int serialize_ucs2le_fixed(FILE *fp, ucs2_char_t* val, size_t length, int is_storing)
{
      size_t i;
      int ret = 0;
      for (i = 0;i < length;i++) {
            ret |= serialize_ucs2le_char(fp, &val[i], is_storing);
      }
      return ret;
}

static char *strip(char *line)
{
      char *p = line;
      while (*p && *p != '\n' && *p != '\r') {
            p++;
      }
      *p = 0;

      return line;
}

void init_tuner(tuner_dat_t* tuner)
{
      memset(tuner, 0, sizeof(*tuner));
      tuner->unknown1 = 1;
      tuner->unknown2 = 0;
      tuner->unknown3 = 1;
}

int validate_tuner_frequency(uint8_t region, uint32_t frequency)
{
      switch (region) {
      case 0:
            return (87500000 <= frequency && frequency < 108100000);
      case 1:
            return (76000000 <= frequency && frequency < 108100000);
      case 2:
            return (87500000 <= frequency && frequency < 108050000);
      case 3:
            return (87500000 <= frequency && frequency < 108100000);
      default:
            return 0;
      }
}

int import_tuner(tuner_dat_t* tuner, FILE *fpi, void *instance, easyh10_error_callback error_proc)
{
      int i = -1;
      int lines = 0;
      char line[1024], *begin = NULL;
      tuner->num_entries = 0;

      while (fgets(line, sizeof(line)-1, fpi)) {
            begin = line;
            strip(begin);

            if (lines == 0) {
                  unsigned char *p = (unsigned char *)begin;
                  if (p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF) {
                        begin += 3;
                  }
            }
            lines++;

            /* Skip an empty line. */
            if (!begin[0]) {
                  continue;
            }

            /* Skip white spaces at the beginning. */
            while (isspace(*begin)) {
                  begin++;
            }

            /* Skip a comment line. */
            if (begin[0] == '#') {
                  if (strncmp(begin, "#$Region=", 9) == 0) {
                        tuner->region = atoi(begin + 9);
                        i = 0;
                  }
                  continue;
            }

            if (0 <= i && i < MAX_STATIONS) {
                  char *p = strchr(begin, ':');

                  /* Initialize this entry. */
                  memset(&tuner->stations[i], 0, sizeof(tuner->stations[i]));

                  /* Parse this line only when it is separated by '\t'. */
                  if (p) {
                        char *q;
                        uint32_t frequency = 0;
                        *p++;

                        /* Read radio-station name. */
                        utf8toucs2(tuner->stations[i].name, 10, p, strlen(p));

                        /* Read the frequency. */
                        frequency= strtoul(begin, &q, 10);
                        switch (*q) {
                        case 'k': case 'K':
                              frequency *= 1000;
                              break;
                        case 'm': case 'M':
                              frequency *= 1000000;
                        }
                        tuner->stations[i].frequency = frequency;

                        if (validate_tuner_frequency(tuner->region, tuner->stations[i].frequency)) {
                              tuner->num_entries++;
                        } else {
                              /* Specified frequency is invalid. */
                              ucs2_char_t* ucs2 = utf8dupucs2(line);
                              error_proc(instance, EASYH10_WARNING_TUNER_TOOMANYENTRIES, ucs2);
                              ucs2free(ucs2);

                              /* Discard this entry when the frequency is invalid. */
                              memset(&tuner->stations[i], 0, sizeof(tuner->stations[i]));
                        }
                        i++;
                  } else {
                        ucs2_char_t* ucs2 = utf8dupucs2(line);
                        error_proc(instance, EASYH10_WARNING_TUNER_INVALIDLINE, ucs2);
                        ucs2free(ucs2);
                  }
            }
      }
      
      return 0;
}

int export_tuner(tuner_dat_t* tuner, FILE *fpo)
{
      int i;

      fprintf(fpo, "\xEF\xBB\xBF");
      fprintf(fpo, "######################################################################\n");
      fprintf(fpo, "# EasyH10 tuner preset\n");
      fprintf(fpo, "######################################################################\n");
      fprintf(fpo, "\n");
      fprintf(fpo, "# Specify the region (frequency band) for FM tuner.\n");
      fprintf(fpo, "#   value = {0: Korea, 1: Japan, 2: Europe, 3: USA}\n");
      fprintf(fpo, "# The following looks like a comment line but does specify the region.\n");
      fprintf(fpo, "#$Region=%d\n", tuner->region);
      fprintf(fpo, "\n");
      fprintf(fpo, "# The rest of this file describes a list of tuner presets. Each line\n");
      fprintf(fpo, "# consists of a freqency of a radio station and its name separated by\n");
      fprintf(fpo, "# a colon character. The default unit of frequency is [Hz]. EasyH10\n");
      fprintf(fpo, "# also supports prefix letters 'k' and 'm' appearing just after digits\n");
      fprintf(fpo, "# to interpret the digits in [kHz] and [MHz]. A station name must be\n");
      fprintf(fpo, "# no longer than 10 letters due to the limitation of H10 players.\n");
      
      for (i = 0;i < tuner->num_entries;i++) {
            char *mbs = ucs2duputf8(tuner->stations[i].name);
            fprintf(fpo, "%d:%s\n", tuner->stations[i].frequency, mbs);
            ucs2free(mbs);
      }

      return 0;
}

int serialize_tuner(tuner_dat_t* tuner, FILE *fp, int is_storing)
{
      int i, ret = 0;

      ret |= serialize_uint8(fp, &tuner->unknown1, is_storing);
      ret |= serialize_uint8(fp, &tuner->unknown2, is_storing);
      ret |= serialize_uint8(fp, &tuner->region, is_storing);
      ret |= serialize_uint8(fp, &tuner->num_entries, is_storing);
      ret |= serialize_uint8(fp, &tuner->unknown3, is_storing);

      for (i = 0;i < MAX_STATIONS;i++) {
            uint8_t index = i;
            uint8_t freq_integer[3+1] = {0,0,0,0};
            uint8_t freq_fractional[2+1] = {0,0,0};
            
            ret |= serialize_uint8(fp, &index, is_storing);
            ret |= serialize_ucs2le_fixed(fp, tuner->stations[i].name, 10, is_storing);

            if (is_storing) {
                  if (validate_tuner_frequency(tuner->region, tuner->stations[i].frequency)) {
                        double frequency = tuner->stations[i].frequency / 1000000. + 0.005;
                        snprintf(
                              freq_integer,
                              sizeof(freq_integer),
                              "%03d",
                              (int)frequency
                              );
                        snprintf(
                              freq_fractional,
                              sizeof(freq_fractional),
                              "%02d",
                              (int)((frequency - (double)(int)frequency) * 100.0)
                              );
                  } else {
                        strcpy(freq_integer, "000");
                        strcpy(freq_fractional, "00");
                  }
            }

            ret |= serialize_uint8(fp, &freq_integer[0], is_storing);
            ret |= serialize_uint8(fp, &freq_integer[1], is_storing);
            ret |= serialize_uint8(fp, &freq_integer[2], is_storing);
            ret |= serialize_uint8(fp, &freq_fractional[0], is_storing);
            ret |= serialize_uint8(fp, &freq_fractional[1], is_storing);

            if (!is_storing) {
                  tuner->stations[i].frequency  = strtoul(freq_integer, NULL, 10) * 1000000;
                  tuner->stations[i].frequency += strtoul(freq_fractional, NULL, 10) * 10000;
            }
      }

      return ret;
}

int merge_tuner(tuner_dat_t* dst, const tuner_dat_t* src)
{
      int i, j, ret = 0;
      
      for (i = 0;i < src->num_entries;++i) {
            const tuner_entry_t* src_entry = &src->stations[i];

            for (j = 0;j < dst->num_entries;++j) {
                  tuner_entry_t* dst_entry = &dst->stations[j];
                  if (ucs2cmp(src_entry->name, dst_entry->name) == 0 && src_entry->frequency == dst_entry->frequency) {
                        break;
                  }
            }
            if (j == dst->num_entries) {
                  if (dst->num_entries < MAX_STATIONS2) {
                        tuner_entry_t* new_entry = &dst->stations[dst->num_entries];
                        ucs2cpy(new_entry->name, src_entry->name);
                        new_entry->frequency = src_entry->frequency;
                        dst->num_entries++;
                  } else {
                        return -1;
                  }
            }
      }
      return 0;
}

static int dummy_progress_callback(void *instance, int phase, int param_int, void *param_ptr)
{
      return 0;
}

static void dummy_error_callback(void *instance, int code, void *param)
{
}

int easyh10_tuner_import(
      const ucs2_char_t* path_to_system,
      const ucs2_char_t* localfile,
      int flag,
      void *instance,
      update_progress_callback progress_proc,
      easyh10_error_callback error_proc
      )
{
      int ret = 0;
      tuner_dat_t tuner;
      FILE *fpi = NULL, *fpo = NULL;
      ucs2_char_t filename[MAX_PATH+1];

      /* Set dummy progress callback if progress_proc was NULL. */
      if (!progress_proc) {
            progress_proc = dummy_progress_callback;
      }

      /* Set dummy error callback if error_proc was NULL. */
      if (!error_proc) {
            error_proc = dummy_error_callback;
      }

      /* Notify the start of importing a tuner preset. */
      progress_proc(instance, EASYH10P_TUNER_IMPORT_START, -1, (void*)localfile);

      init_tuner(&tuner);

      /*
       * Read the tuner preset in the local side.
       */
      fpi = ucs2fopen(localfile, "r");
      if (!fpi) {
            return EASYH10_ERROR_TUNERLOCALOPEN;
      }
      ret = import_tuner(&tuner, fpi, instance, error_proc);
      if (ret != 0) {
            return ret;
      }
      fclose(fpi);

      /* Notify the start of importing a tuner preset. */
      progress_proc(instance, EASYH10P_TUNER_IMPORT, tuner.num_entries, (void*)localfile);

      ucs2cpy(filename, path_to_system);
      filepathutil_addslash(filename);
      ucs2cat(filename, ucs2cs_tuner_dat);

      if (flag & EASYH10_TUNER_MERGE) {
            tuner_dat_t old_tuner;
            init_tuner(&old_tuner);

            /*
             * Read the current tuner preset in the player for merging.
             */
            fpi = ucs2fopen(filename, "rb");
            if (!fpi) {
                  return EASYH10_ERROR_TUNEROPEN;
            }
            ret = serialize_tuner(&old_tuner, fpi, 0);
            if (ret != 0) {
                  return ret;
            }
            fclose(fpi);

            /*
             * Merge the tuner presets.
             */
            ret = merge_tuner(&tuner, &old_tuner);
            if (ret != 0) {
                  return EASYH10_ERROR_TUNERMERGE;
            }

            /* Notify the start of importing a tuner preset. */
            progress_proc(instance, EASYH10P_TUNER_IMPORT_MERGE, tuner.num_entries, (void*)filename);
      }

      if (tuner.num_entries > MAX_STATIONS) {
            error_proc(instance, EASYH10_WARNING_TUNER_TOOMANYENTRIES, NULL);
      }
      
      /*
       * Write the tuner preset to the player.
       */
      fpo = ucs2fopen(filename, "wb");
      if (!fpo) {
            return EASYH10_ERROR_TUNEROPEN;
      }
      ret = serialize_tuner(&tuner, fpo, 1);
      if (ret != 0) {
            return ret;
      }
      fclose(fpo);

      /* Notify the start of importing a tuner preset. */
      progress_proc(instance, EASYH10P_TUNER_IMPORT_END, -1, NULL);

      return 0;
}

int easyh10_tuner_export(
      const ucs2_char_t* path_to_system,
      const ucs2_char_t* localfile,
      int flag,
      void *instance,
      update_progress_callback progress_proc,
      easyh10_error_callback error_proc
      )
{
      int ret = 0;
      tuner_dat_t tuner;
      FILE *fpi = NULL, *fpo = NULL;
      ucs2_char_t filename[MAX_PATH+1];

      /* Set dummy progress callback if progress_proc was NULL. */
      if (!progress_proc) {
            progress_proc = dummy_progress_callback;
      }

      /* Set dummy error callback if error_proc was NULL. */
      if (!error_proc) {
            error_proc = dummy_error_callback;
      }

      init_tuner(&tuner);

      ucs2cpy(filename, path_to_system);
      filepathutil_addslash(filename);
      ucs2cat(filename, ucs2cs_tuner_dat);
      
      /* Notify the start of importing a tuner preset. */
      progress_proc(instance, EASYH10P_TUNER_EXPORT_START, -1, (void*)filename);

      /*
       * Read the current tuner preset from the player.
       */
      fpi = ucs2fopen(filename, "rb");
      if (!fpi) {
            return EASYH10_ERROR_TUNEROPEN;
      }
      ret = serialize_tuner(&tuner, fpi, 0);
      if (ret != 0) {
            return ret;
      }
      fclose(fpi);

      /* Notify the start of importing a tuner preset. */
      progress_proc(instance, EASYH10P_TUNER_EXPORT, tuner.num_entries, (void*)filename);

      if (flag & EASYH10_TUNER_MERGE) {
            tuner_dat_t old_tuner;

            /*
             * Read the tuner preset from the local file.
             */
            fpi = ucs2fopen(localfile, "r");
            if (!fpi) {
                  return EASYH10_ERROR_TUNERLOCALOPEN;
            }
            ret = import_tuner(&old_tuner, fpi, instance, error_proc);
            if (ret != 0) {
                  return ret;
            }
            fclose(fpi);

            /*
             * Merge the tuner preset.
             */
            ret = merge_tuner(&tuner, &old_tuner);
            if (ret != 0) {
                  return EASYH10_ERROR_TUNERMERGE;
            }

            /* Notify the start of importing a tuner preset. */
            progress_proc(instance, EASYH10P_TUNER_EXPORT_MERGE, tuner.num_entries, (void*)localfile);
      }

      /*
       * Write the tuner preset to the local file.
       */
      fpo = ucs2fopen(localfile, "w");
      if (!fpo) {
            return EASYH10_ERROR_TUNERLOCALOPEN;
      }
      ret = export_tuner(&tuner, fpo);
      if (ret != 0) {
            return ret;
      }

      /* Notify the start of importing a tuner preset. */
      progress_proc(instance, EASYH10P_TUNER_EXPORT_END, -1, NULL);

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index