/*      
  _____     ___ ____ 
   ____|   |    ____|      PSX2 OpenSource Project
  |     ___|   |____       (c) 2008 jMk0Nm 
------------------------------------------------------------------------
osdname.c

Unpacks rom0:osdsys and tries to find osdxxx strings.

*/

#include "mcboot.h"


typedef struct {
  int size;
  u8 *addr;
} FileInfo_t;

u8 *find_osd_string(u8 *buf, u32 bufsize);
u8 *find_osdpack_start(u8 *osdsys_addr);
int load_file(const char *path, FileInfo_t *finf);
u32 OSDUnpack(u8 *pack_start, u8 *unpack_start);



u8 *Get_OSD_Name(void)
{
  FileInfo_t osdsys;
  u8 *pack_start, *unpack_start = NULL, *ret = NULL;
  u32 unpacked_size;

  if (!load_file("rom0:OSDSYS", &osdsys))
    return NULL;

  if ((pack_start = find_osdpack_start(osdsys.addr))) {
    unpacked_size = *(u32*)pack_start;
    printf("\tUnpacked size: %d\n", unpacked_size);
    if ((unpack_start = malloc(unpacked_size * 2))) {
      printf("\tUnpacking...\n");
      OSDUnpack(pack_start, unpack_start);

      printf("\tChecking unpacked OSDSYS...\n");
      ret = find_osd_string(unpack_start, unpacked_size);
    }
  }
  if (!ret) {
    printf("\tChecking raw OSDSYS...\n");
    ret = find_osd_string(osdsys.addr, osdsys.size);
  }
  if (unpack_start)
    free(unpack_start);
  free(osdsys.addr);
  return ret;
}

/**********************************************************************************/

u8 *find_osd_string(u8 *buf, u32 bufsize)
{
  u32 i, j;
  const u8 *s, *p;
  static u8 result[16];

  static const u8 *osdstrings[] = {	// add more strings if needed
    "osdmain.elf",
    "osdmain.sys",
    "osdsys.elf",
    "osd###.elf",
    "osd###.sys",
    "\n"
  };

  for (i = 0; *osdstrings[i] != '\n'; i++) {
    for (j = 0; j < bufsize; j++) {
      s = osdstrings[i];
      for (p = buf + j; *s && (*s == *p || *s == '#'); s++, p++);
      if (!*s) {
        printf("\tString found: %s\n", osdstrings[i]);
        strncpy(result, buf + j, strlen(osdstrings[i]));
        return (result);   // found!
      }
    }
  }
  return NULL;
}

/**********************************************************************************/

u8 *find_osdpack_start(u8 *osdsys_addr)
{
  int i;
  u16 section_hdr_num = *(u16*)&osdsys_addr[48];
  u32 section_hdr_offset = *(u32*)&osdsys_addr[32];
  u32 attrib;

//  printf("\tSection header num:  %d\n", section_hdr_num);
//  printf("\tSection header offs: %08x\n", section_hdr_offset);

  for (i = 0; i < section_hdr_num; i++) {
    attrib = *(u32*)&osdsys_addr[section_hdr_offset + 8];
    if (attrib == 3)
      return (osdsys_addr + *(u32*)&osdsys_addr[section_hdr_offset + 16]);
    section_hdr_offset += 40;
  }
  return NULL;
}

/**********************************************************************************/

int load_file(const char *path, FileInfo_t *finf)
{
  int fd;

  if ((fd = fioOpen(path, O_RDONLY)) < 0) {
    printf("\tERROR: Failed to open file '%s'\n", path);
    return 0;
  }
  finf->size = fioLseek(fd, 0, SEEK_END);
  printf("\tFile '%s' size: %d\n", path, finf->size);
  fioLseek(fd, 0, SEEK_SET);

  // alloc x2 for safety
  if (!(finf->addr = malloc(finf->size * 2))) {
    printf("\tERROR: Failed to malloc %d bytes for '%s'\n", finf->size, path);
    fioClose(fd);
    return 0;
  }
  fioRead(fd, finf->addr, finf->size);
  fioClose(fd);
  return 1;
}

/**********************************************************************************/


u32 OSDUnpack(u8 *pack_start, u8 *unpack_start)
{
  int hash = 0;
  u32 unpacked_size, shift_amount = 0, offset_mask = 0, count, offset, numbytes;
  u8 *pack_ptr, *src, *dest;
  u8 c1, c2;

  unpacked_size = *(u32*)pack_start;
  pack_ptr = pack_start + 4;
  dest = unpack_start;
  count = 0;

  for (;;) {

    if (count == 0) {
      count = 30;
      hash = *(pack_ptr++) << 8;
      hash = (hash | *(pack_ptr++)) << 8;
      hash = (hash | *(pack_ptr++)) << 8;
      hash = hash | *(pack_ptr++);
      offset_mask = 0x3fff >> (hash & 3);
      shift_amount = 14 - (hash & 3);
    }

    c1 = *(pack_ptr++);

    if (hash >= 0)		// sign bit set?
      *(dest++) = c1;
    else {
      c2 = *(pack_ptr++);
      offset = (((c1 << 8) | c2) & offset_mask) + 1;
      numbytes = (((c1 << 8) | c2) >> shift_amount) + 2;
      src = dest - offset;
      *(dest++) = *(src++);
      while (numbytes != 0) {
        *(dest++) = *(src++);
        numbytes--;
      }
    }

    count--;
    if (dest - unpack_start >= unpacked_size)
      break;
    hash <<= 1;
  }
  return unpacked_size;
}

