/*
 * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file picoos.c
 *
 * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
 * All rights reserved.
 *
 * History:
 * - 2009-04-20 -- initial version
 *
 */

#include <stdarg.h>
#include "picodefs.h"
#include "picopal.h"
#include "picoos.h"
#include "picodbg.h"

#ifdef __cplusplus
extern "C" {
#endif
#if 0
}
#endif


#define picoos_SVOXFileHeader (picoos_char *)" (C) SVOX AG "

/* **********************************************
 * default error and warning messages
 * **********************************************/


#define PICOOS_MSG_EXC_NUMBER_FORMAT  (picoos_char *)  "wrong number format"
#define PICOOS_MSG_EXC_MAX_NUM_EXCEED (picoos_char *)  "number exceeded"
#define PICOOS_MSG_EXC_NAME_CONFLICT  (picoos_char *)  "name conflict"
#define PICOOS_MSG_EXC_NAME_UNDEFINED (picoos_char *)  "name undefined"
#define PICOOS_MSG_EXC_NAME_ILLEGAL   (picoos_char *)  "illegal name"

/* buffer interaction */
#define PICOOS_MSG_EXC_BUF_OVERFLOW   (picoos_char *)   "buffer overflow"
#define PICOOS_MSG_EXC_BUF_UNDERFLOW  (picoos_char *)   "buffer underflow"
#define PICOOS_MSG_EXC_BUF_IGNORE     (picoos_char *)   "buffer error"

/* memory allocation */
#define PICOOS_MSG_EXC_OUT_OF_MEM     (picoos_char *)   "out of memory"

/* files */
#define PICOOS_MSG_EXC_CANT_OPEN_FILE       (picoos_char *) "cannot open file"
#define PICOOS_MSG_EXC_UNEXPECTED_FILE_TYPE (picoos_char *) "unexpected file type"
#define PICOOS_MSG_EXC_FILE_CORRUPT         (picoos_char *) "corrupt file"
#define PICOOS_MSG_EXC_FILE_NOT_FOUND       (picoos_char *) "file not found"

/* resources */
#define PICOOS_MSG_EXC_RESOURCE_BUSY         (picoos_char *)  "resource is busy"
#define PICOOS_MSG_EXC_RESOURCE_MISSING      (picoos_char *)  "cannot find resource"

/* knowledge bases */
#define PICOOS_MSG_EXC_KB_MISSING     (picoos_char *)  "knowledge base missing"

/* runtime exceptions (programming problems, usually a bug. E.g. trying to access null pointer) */
#define PICOOS_MSG_ERR_NULLPTR_ACCESS     (picoos_char *)   "access violation"
#define PICOOS_MSG_ERR_INVALID_HANDLE     (picoos_char *)   "invalid handle value"
#define PICOOS_MSG_ERR_INVALID_ARGUMENT   (picoos_char *)   "invalid argument supplied"
#define PICOOS_MSG_ERR_INDEX_OUT_OF_RANGE (picoos_char *)   "index out of range"


/* errors ("external" errors, e.g. hardware failure. Usually cannot be dealt with from inside pico.) */
#define PICOOS_MSG_ERR_OTHER         (picoos_char *) "other error"

#define PICOOS_MSG_ERR_PU            (picoos_char *) "error in processing unit"

/* WARNINGS */

/* general */
#define PICOOS_MSG_WARN_INCOMPLETE    (picoos_char *)  "incomplete output"
#define PICOOS_MSG_WARN_FALLBACK      (picoos_char *)  "using fall-back"
#define PICOOS_MSG_WARN_OTHER         (picoos_char *)  "other warning"

/* resources */
#define PICOOS_MSG_WARN_KB_OVERWRITE          (picoos_char *)  "overwriting knowledge base"
#define PICOOS_MSG_WARN_RESOURCE_DOUBLE_LOAD  (picoos_char *)  "resource already loaded"

/* decision trees */
#define PICOOS_MSG_WARN_INVECTOR        (picoos_char *)  "input vector not constructed"
#define PICOOS_MSG_WARN_CLASSIFICATION  (picoos_char *)  "output not classified"
#define PICOOS_MSG_WARN_OUTVECTOR       (picoos_char *)  "output vector not decomposed"

/* processing units */
#define PICOOS_MSG_WARN_PU_IRREG_ITEM   (picoos_char *)  "irregular item in processing unit"
#define PICOOS_MSG_WARN_PU_DISCARD_BUF  (picoos_char *)  "discarding processing unit buffer"


/* **********************************************
 * wrappers for picopal functions
 * **********************************************/

picoos_int32 picoos_atoi(const picoos_char *s)
{
    return (picoos_int32)picopal_atoi((const picoos_char *)s);
}


picoos_int8 picoos_strcmp(const picoos_char *a, const picoos_char *b)
{
    picopal_int32 res = picopal_strcmp((const picopal_char *)a,
            (const picopal_char *)b);
    return (picoos_int8) ((res < 0) ? -1 : (res > 0) ? 1 : 0);
}
picoos_int8 picoos_strncmp(const picoos_char *a, const picoos_char *b, picoos_objsize_t siz)
{
    picopal_int32 res = picopal_strncmp((const picopal_char *)a,
            (const picopal_char *)b, siz);
    return (picoos_int8) ((res < 0) ? -1 : (res > 0) ? 1 : 0);
}

picoos_uint32 picoos_strlen(const picoos_char *s)
{
    return (picoos_uint32)picopal_strlen((const picopal_char *)s);
}

picoos_char *picoos_strchr(const picoos_char *s, picoos_char c)
{
    return (picoos_char *)picopal_strchr((const picopal_char *)s,
            (picopal_char)c);
}

picoos_char *picoos_strstr(const picoos_char *s, const picoos_char * substr)
{
    return (picoos_char *)picopal_strstr((const picopal_char *)s,
            (const picopal_char *)substr);
}

picoos_int16 picoos_slprintf(picoos_char * b, picoos_uint32 bsize, const picoos_char *f, ...)
{
    picopal_int16 i;
    va_list args;

    va_start(args, (char *)f);
    i = (picoos_int16)picopal_vslprintf((picoos_char *) b, bsize, (const picoos_char *)f, args);
    va_end(args);
    return i;
}

picoos_char *picoos_strcpy(picoos_char *d, const picoos_char *s)
{
    return (picoos_char *)picopal_strcpy((picopal_char *)d,
            (const picopal_char *)s);
}

picoos_char *picoos_strcat(picoos_char *d, const picoos_char *s)
{
    return (picoos_char *)picopal_strcat((picopal_char *)d,
            (const picopal_char *)s);
}

picoos_objsize_t picoos_strlcpy(picoos_char *dst, const picoos_char *src, picoos_objsize_t siz)
{
    return (picoos_objsize_t) picopal_strlcpy((picopal_char *) dst, (const picopal_char *) src, (picopal_objsize_t) siz);
}

/* copies 'length' bytes from 'src' to 'dest'. (regions may be overlapping) no error checks! */
void * picoos_mem_copy(const void * src, void * dst,  picoos_objsize_t length)
{
    return picopal_mem_copy(src,dst,(picopal_objsize_t) length);
}

/* sets 'length' bytes starting at dest[0] to 'byte_val' */
void * picoos_mem_set(void * dest, picoos_uint8 byte_val, picoos_objsize_t length) {
          return picopal_mem_set(dest,(picopal_uint8)byte_val, (picopal_objsize_t)length);
}


picoos_double picoos_cos (const picoos_double cos_arg)
{
    return (picoos_double) picopal_cos ((picopal_double) cos_arg);
}


picoos_double picoos_sin (const picoos_double sin_arg)
{
    return (picoos_double) picopal_sin((picopal_double) sin_arg);
}
picoos_double picoos_fabs (const picoos_double fabs_arg)
{
    return (picoos_double) picopal_fabs((picopal_double) fabs_arg);
}

picoos_double picoos_quick_exp(const picoos_double y) {
    return (picoos_double) picopal_quick_exp ((picopal_double)y);
}


/* *****************************************************************/
/* "Common"                                                        */
/* *****************************************************************/
/* picoos_common is a collection of basic functionalities that must be globally
 * accessible from every "big" function. It includes pointers to the MemoryManasger,
 * ExceptionManager and a system-wide list of open files. */

picoos_Common picoos_newCommon(picoos_MemoryManager mm)
{
    picoos_Common this = (picoos_Common) picoos_allocate(mm,sizeof(*this));
    if (NULL != this) {
        /* initialize */
        this->em = NULL;
        this->mm = NULL;
        this->fileList = NULL;
    }
    return this;
}

void picoos_disposeCommon(picoos_MemoryManager mm, picoos_Common * this)
{
    if (NULL != (*this)) {
        /* terminate */
        picoos_deallocate(mm,(void *)this);
    }
}


/* *****************************************************************/
/* Memory Management                                               */
/* *****************************************************************/

typedef struct mem_block_hdr * MemBlockHdr;
typedef struct mem_block_hdr
{
    MemBlockHdr next;
    byte_ptr_t data;
    picoos_objsize_t size;
} mem_block_hdr_t;

typedef struct mem_cell_hdr * MemCellHdr;
typedef struct mem_cell_hdr
{
    /* size may be <0 if used */
    picoos_ptrdiff_t size;
    MemCellHdr leftCell;
    MemCellHdr prevFree, nextFree;
} mem_cell_hdr_t;

typedef struct memory_manager
{
    MemBlockHdr firstBlock, lastBlock; /* memory blockList */
    MemCellHdr freeCells, lastFree; /* free memory cells (first/last sentinel */
    /* "constants" */
    picoos_objsize_t fullCellHdrSize; /* aligned size of full cell header, including free-links */
    picoos_objsize_t usedCellHdrSize; /* aligned size of header part without free-links */
    picoos_objsize_t minContSize; /* minimum requestable application content size for allocation;
     must hold free-list info; = fullCellHdrSize-usedCellHdrSize */
    picoos_objsize_t minCellSize; /* minimum remaining cell size when a free cell is split */
    picoos_bool protMem;  /* true if memory protection is enabled */
    picoos_ptrdiff_t usedSize;
    picoos_ptrdiff_t prevUsedSize;
    picoos_ptrdiff_t maxUsedSize;
} memory_manager_t;

/** allocates 'alloc_size' bytes at start of raw memory block ('raw_mem',raw_mem_size)
 *  and returns pointer to allocated region. Returns remaining (correctly aligned) raw memory block
 *  in ('rest_mem','rest_mem_size').
 *  The allocated memory is not subject to memory management, so that it can never be freed again!
 *
 */
void * picoos_raw_malloc(byte_ptr_t raw_mem,
        picoos_objsize_t raw_mem_size, picoos_objsize_t alloc_size,
        byte_ptr_t * rest_mem, picoos_objsize_t * rest_mem_size)
{
    picoos_ptrdiff_t rest;
    if (raw_mem == NULL) {
        return NULL;
    } else {
        if (alloc_size < 1) {
            alloc_size = 1;
        }
        alloc_size = ((alloc_size + PICOOS_ALIGN_SIZE - 1) / PICOOS_ALIGN_SIZE)
                * PICOOS_ALIGN_SIZE;

        rest = raw_mem_size - alloc_size;
        if (rest < 0) {
            return NULL;
        } else {
            *rest_mem_size = rest;
            *rest_mem = raw_mem + alloc_size;
            return (void *) raw_mem;
        }
    }
}

/** initializes the last block of mm */
static int os_init_mem_block(picoos_MemoryManager this)
{
    int isFirstBlock;
    void * newBlockAddr;
    picoos_objsize_t size;
    MemCellHdr cbeg, cmid, cend;

    isFirstBlock = (this->freeCells == NULL);
    newBlockAddr = (void *) this->lastBlock->data;
    size = this->lastBlock->size;
    cbeg = (MemCellHdr) newBlockAddr;
    cmid = (MemCellHdr)((picoos_objsize_t)newBlockAddr + this->fullCellHdrSize);
    cend = (MemCellHdr)((picoos_objsize_t)newBlockAddr + size
            - this->fullCellHdrSize);
    cbeg->size = 0;

    cbeg->leftCell = NULL;
    cmid->size = size - 2 * this->fullCellHdrSize;
    cmid->leftCell = cbeg;
    cend->size = 0;
    cend->leftCell = cmid;
    if (isFirstBlock) {
        cbeg->nextFree = cmid;
        cbeg->prevFree = NULL;
        cmid->nextFree = cend;
        cmid->prevFree = cbeg;
        cend->nextFree = NULL;
        cend->prevFree = cmid;
        this->freeCells = cbeg;
        this->lastFree = cend;
    } else {
        /* add cmid to free cell list */
        cbeg->nextFree = NULL;
        cbeg->prevFree = NULL;
        cmid->nextFree = this->freeCells->nextFree;
        cmid->prevFree = this->freeCells;
        cmid->nextFree->prevFree = cmid;
        cmid->prevFree->nextFree = cmid;
        cend->nextFree = NULL;
        cbeg->prevFree = NULL;
    }
    return PICO_OK;
}


picoos_MemoryManager picoos_newMemoryManager(
        void *raw_memory,
        picoos_objsize_t size,
        picoos_bool enableMemProt)
{
    byte_ptr_t rest_mem;
    picoos_objsize_t rest_mem_size;
    picoos_MemoryManager this;
    picoos_objsize_t size2;
    mem_cell_hdr_t test_cell;

    this = picoos_raw_malloc(raw_memory, size, sizeof(memory_manager_t),
            &rest_mem, &rest_mem_size);
    if (this == NULL) {
        return NULL;
    }

    /* test if memory protection functionality is available on the current
       platform (if not, picopal_mpr_alloc() always returns NULL) */
    if (enableMemProt) {
        void *addr = picopal_mpr_alloc(100);
        if (addr == NULL) {
            enableMemProt = FALSE;
        } else {
            picopal_mpr_free(&addr);
        }
    }

    this->firstBlock = NULL;
    this->lastBlock = NULL;
    this->freeCells = NULL;
    this->lastFree = NULL;

    this->protMem = enableMemProt;
    this->usedSize = 0;
    this->prevUsedSize = 0;
    this->maxUsedSize = 0;

    /* get aligned full header size */
    this->fullCellHdrSize = ((sizeof(mem_cell_hdr_t) + PICOOS_ALIGN_SIZE - 1)
            / PICOOS_ALIGN_SIZE) * PICOOS_ALIGN_SIZE;
    /* get aligned size of header without free-list fields; the result may be compiler-dependent;
     the size is therefore computed by inspecting the end addresses of the fields 'size' and 'leftCell';
     the higher of the ending addresses is used to get the (aligned) starting address
     of the application contents */
    this->usedCellHdrSize = (picoos_objsize_t) &test_cell.size
            - (picoos_objsize_t) &test_cell + sizeof(picoos_objsize_t);
    size2 = (picoos_objsize_t) &test_cell.leftCell - (picoos_objsize_t)
            &test_cell + sizeof(MemCellHdr);
    if (size2 > this->usedCellHdrSize) {
        this->usedCellHdrSize = size2;
    }
    /* get minimum application-usable size; must be large enough to hold remainder of
     cell header (free-list links) when in free-list */
    this->minContSize = this->fullCellHdrSize - this->usedCellHdrSize;
    /* get minimum required size of a cell remaining after a cell split */
    this->minCellSize = this->fullCellHdrSize + PICOOS_ALIGN_SIZE;

    /* install remainder of raw memory block as first block */
    raw_memory = rest_mem;
    size = rest_mem_size;
    this->firstBlock = this->lastBlock = picoos_raw_malloc(raw_memory, size,
            sizeof(mem_block_hdr_t), &rest_mem, &rest_mem_size);
    if (this->lastBlock == NULL) {
        return NULL;
    }
    this->lastBlock->next = NULL;
    this->lastBlock->data = rest_mem;
    this->lastBlock->size = rest_mem_size;

    os_init_mem_block(this);

    return this;
}

void picoos_disposeMemoryManager(picoos_MemoryManager * mm)
{
    *mm = NULL;
}


/* the following memory manager routines are for testing and
   debugging purposes */


void *picoos_allocProtMem(picoos_MemoryManager mm, picoos_objsize_t byteSize)
{
    if (mm->protMem) {
        return picopal_mpr_alloc(byteSize);
    } else {
        return picoos_allocate(mm, byteSize);
    }
}


void picoos_deallocProtMem(picoos_MemoryManager mm, void **addr)
{
    if (mm->protMem) {
        picopal_mpr_free(addr);
    } else {
        picoos_deallocate(mm, addr);
    }
}


void picoos_protectMem(
        picoos_MemoryManager mm,
        void *addr,
        picoos_objsize_t len,
        picoos_bool enable)
{
    if (mm->protMem) {
        int prot = PICOPAL_PROT_READ;
        if (!enable) {
            prot |= PICOPAL_PROT_WRITE;
        }
        picopal_mpr_protect(addr, len, prot);
    } else {
        /* memory protection disabled; nothing to do */
    }
}

#define PICOPAL_PROT_NONE   0   /* the memory cannot be accessed at all */
#define PICOPAL_PROT_READ   1   /* the memory can be read */
#define PICOPAL_PROT_WRITE  2   /* the memory can be written to */

void picoos_getMemUsage(
        picoos_MemoryManager this,
        picoos_bool resetIncremental,
        picoos_int32 *usedBytes,
        picoos_int32 *incrUsedBytes,
        picoos_int32 *maxUsedBytes)
{
    *usedBytes = (picoos_int32) this->usedSize;
    *incrUsedBytes = (picoos_int32) (this->usedSize - this->prevUsedSize);
    *maxUsedBytes = (picoos_int32) this->maxUsedSize;
    if (resetIncremental) {
        this->prevUsedSize = this->usedSize;
    }
}


void picoos_showMemUsage(picoos_MemoryManager this, picoos_bool incremental,
        picoos_bool resetIncremental)
{
    picoos_int32 usedBytes, incrUsedBytes, maxUsedBytes;

    picoos_getMemUsage(this, resetIncremental, &usedBytes, &incrUsedBytes,
            &maxUsedBytes);
    if (incremental) {
        PICODBG_DEBUG(("additional memory used: %d", incrUsedBytes));
    } else {
        PICODBG_DEBUG(("memory used: %d, maximally used: %d", usedBytes, maxUsedBytes));
    }
}


void * picoos_allocate(picoos_MemoryManager this,
        picoos_objsize_t byteSize)
{

    picoos_objsize_t cellSize;
    MemCellHdr c, c2, c2r;
    void * adr;

    if (byteSize < this->minContSize) {
        byteSize = this->minContSize;
    }
    byteSize = ((byteSize + PICOOS_ALIGN_SIZE - 1) / PICOOS_ALIGN_SIZE)
            * PICOOS_ALIGN_SIZE;

    cellSize = byteSize + this->usedCellHdrSize;
    /*PICODBG_TRACE(("allocating %d", cellSize));*/
    c = this->freeCells->nextFree;
    while (
            (c != NULL) &&
            (c->size != (picoos_ptrdiff_t) cellSize) &&
            (c->size < (picoos_ptrdiff_t)(cellSize+ this->minCellSize))) {
        c = c->nextFree;
    }
    if (c == NULL) {
        return NULL;
    }
    if ((c->size == (picoos_ptrdiff_t) cellSize)) {
        c->prevFree->nextFree = c->nextFree;
        c->nextFree->prevFree = c->prevFree;
    } else {
        c2 = (MemCellHdr)((picoos_objsize_t)c + cellSize);
        c2->size = c->size - cellSize;
        c->size = cellSize;
        c2->leftCell = c;
        c2r = (MemCellHdr)((picoos_objsize_t)c2 + c2->size);
        c2r->leftCell = c2;
        c2->nextFree = c->nextFree;
        c2->nextFree->prevFree = c2;
        c2->prevFree = c->prevFree;
        c2->prevFree->nextFree = c2;
    }

    /* statistics */
    this->usedSize += cellSize;
    if (this->usedSize > this->maxUsedSize) {
        this->maxUsedSize = this->usedSize;
    }

    c->size = -(c->size);
    adr = (void *)((picoos_objsize_t)c + this->usedCellHdrSize);
    return adr;
}

void picoos_deallocate(picoos_MemoryManager this, void * * adr)
{
    MemCellHdr c;
    MemCellHdr cr;
    MemCellHdr cl;
    MemCellHdr crr;


    if ((*adr) != NULL) {
        c = (MemCellHdr)((picoos_objsize_t)(*adr) - this->usedCellHdrSize);
        c->size = -(c->size);

        /*PICODBG_TRACE(("deallocating %d", c->size));*/
        /* statistics */
        this->usedSize -= c->size;

        cr = (MemCellHdr)((picoos_objsize_t)c + c->size);
        cl = c->leftCell;
        if (cl->size > 0) {
            if (cr->size > 0) {
                crr = (MemCellHdr)((picoos_objsize_t)cr + cr->size);
                crr->leftCell = cl;
                cl->size = ((cl->size + c->size) + cr->size);
                cr->nextFree->prevFree = cr->prevFree;
                cr->prevFree->nextFree = cr->nextFree;
            } else {
                cl->size = (cl->size + c->size);
                cr->leftCell = cl;
            }
        } else {
            if ((cr->size > 0)) {
                crr = (MemCellHdr)((picoos_objsize_t)cr + cr->size);
                crr->leftCell = c;
                c->size = (c->size + cr->size);
                c->nextFree = cr->nextFree;
                c->prevFree = cr->prevFree;
                c->nextFree->prevFree = c;
                c->prevFree->nextFree = c;
            } else {
                c->nextFree = this->freeCells->nextFree;
                c->prevFree = this->freeCells;
                c->nextFree->prevFree = c;
                c->prevFree->nextFree = c;
            }
        }
    }
    *adr = NULL;
}

/* *****************************************************************/
/* Exception Management                                                */
/* *****************************************************************/
/**  object   : exceptionManager
 *   shortcut : em
 *
 */

typedef picoos_char picoos_exc_msg[PICOOS_MAX_EXC_MSG_LEN];
typedef picoos_char picoos_warn_msg[PICOOS_MAX_WARN_MSG_LEN];

typedef struct picoos_exception_manager
{
    picoos_int32 curExceptionCode;
    picoos_exc_msg curExceptionMessage;

    picoos_uint8 curNumWarnings;
    picoos_int32 curWarningCode[PICOOS_MAX_NUM_WARNINGS];
    picoos_warn_msg curWarningMessage[PICOOS_MAX_NUM_WARNINGS];

} picoos_exception_manager_t;

void picoos_emReset(picoos_ExceptionManager this)
{
    this->curExceptionCode = PICO_OK;
    this->curExceptionMessage[0] = '\0';
    this->curNumWarnings = 0;
}

picoos_ExceptionManager picoos_newExceptionManager(picoos_MemoryManager mm)
{
    picoos_ExceptionManager this = (picoos_ExceptionManager) picoos_allocate(
            mm, sizeof(*this));
    if (NULL != this) {
        /* initialize */
        picoos_emReset(this);
    }
    return this;
}

void picoos_disposeExceptionManager(picoos_MemoryManager mm,
        picoos_ExceptionManager * this)
{
    if (NULL != (*this)) {
        /* terminate */
        picoos_deallocate(mm, (void *)this);
    }
}

static void picoos_vSetErrorMsg(picoos_char * dst, picoos_objsize_t siz,
        picoos_int16 code, picoos_char * base, const picoos_char *fmt, va_list args)
{
    picoos_uint16 bsize;

    if (NULL == base) {
        switch (code) {
            case PICO_EXC_NUMBER_FORMAT:
                base = PICOOS_MSG_EXC_NUMBER_FORMAT;
                break;
            case PICO_EXC_MAX_NUM_EXCEED:
                base = PICOOS_MSG_EXC_MAX_NUM_EXCEED;
                break;
            case PICO_EXC_NAME_CONFLICT:
                base = PICOOS_MSG_EXC_NAME_CONFLICT;
                break;
            case PICO_EXC_NAME_UNDEFINED:
                base = PICOOS_MSG_EXC_NAME_UNDEFINED;
                break;
            case PICO_EXC_NAME_ILLEGAL:
                base = PICOOS_MSG_EXC_NAME_ILLEGAL;
                break;

                /* buffer interaction */
            case PICO_EXC_BUF_OVERFLOW:
                base = PICOOS_MSG_EXC_BUF_OVERFLOW;
                break;
            case PICO_EXC_BUF_UNDERFLOW:
                base = PICOOS_MSG_EXC_BUF_UNDERFLOW;
                break;
            case PICO_EXC_BUF_IGNORE:
                base = PICOOS_MSG_EXC_BUF_IGNORE;
                break;

                /* memory allocation */
            case PICO_EXC_OUT_OF_MEM:
                base = PICOOS_MSG_EXC_OUT_OF_MEM;
                break;

                /* files */
            case PICO_EXC_CANT_OPEN_FILE:
                base = PICOOS_MSG_EXC_CANT_OPEN_FILE;
                break;
            case PICO_EXC_UNEXPECTED_FILE_TYPE:
                base = PICOOS_MSG_EXC_UNEXPECTED_FILE_TYPE;
                break;
            case PICO_EXC_FILE_CORRUPT:
                base = PICOOS_MSG_EXC_FILE_CORRUPT;
                break;

            case PICO_EXC_FILE_NOT_FOUND:
                base = PICOOS_MSG_EXC_FILE_NOT_FOUND;
                break;

                /* resources */
            case PICO_EXC_RESOURCE_BUSY:
                base = PICOOS_MSG_EXC_RESOURCE_BUSY;
                break;
            case PICO_EXC_RESOURCE_MISSING:
                base = PICOOS_MSG_EXC_RESOURCE_MISSING;
                break;

                /* knowledge bases */
            case PICO_EXC_KB_MISSING:
                fmt = PICOOS_MSG_EXC_KB_MISSING;
                break;

                /* runtime exceptions (programming problems, usually a bug. E.g. trying to access null pointer) */
            case PICO_ERR_NULLPTR_ACCESS:
                base = PICOOS_MSG_ERR_NULLPTR_ACCESS;
                break;
            case PICO_ERR_INVALID_HANDLE:
                base = PICOOS_MSG_ERR_INVALID_HANDLE;
                break;
            case PICO_ERR_INVALID_ARGUMENT:
                base = PICOOS_MSG_ERR_INVALID_ARGUMENT;
                break;
            case PICO_ERR_INDEX_OUT_OF_RANGE:
                base = PICOOS_MSG_ERR_INDEX_OUT_OF_RANGE;
                break;

                /* errors ("external" errors, e.g. hardware failure. Usually cannot be dealt with from inside pico.) */
            case PICO_ERR_OTHER:
                base = PICOOS_MSG_ERR_OTHER;
                break;

                /* other error inside pu */
            case PICO_STEP_ERROR:
                base = PICOOS_MSG_ERR_PU;
                break;

                /* WARNINGS */

                /* general */
            case PICO_WARN_INCOMPLETE:
                base = PICOOS_MSG_WARN_INCOMPLETE;
                break;
            case PICO_WARN_FALLBACK:
                base = PICOOS_MSG_WARN_FALLBACK;
                break;

            case PICO_WARN_OTHER:
                base = PICOOS_MSG_WARN_OTHER;
                break;

                /* resources */
            case PICO_WARN_KB_OVERWRITE:
                base = PICOOS_MSG_WARN_KB_OVERWRITE;
                break;
            case PICO_WARN_RESOURCE_DOUBLE_LOAD:
                base = PICOOS_MSG_WARN_RESOURCE_DOUBLE_LOAD;
                break;

                /* decision trees */
            case PICO_WARN_INVECTOR:
                base = PICOOS_MSG_WARN_INVECTOR;
                break;
            case PICO_WARN_CLASSIFICATION:
                base = PICOOS_MSG_WARN_CLASSIFICATION;
                break;
            case PICO_WARN_OUTVECTOR:
                base = PICOOS_MSG_WARN_OUTVECTOR;
                break;

                /* processing units */
            case PICO_WARN_PU_IRREG_ITEM:
                base = PICOOS_MSG_WARN_PU_IRREG_ITEM;
                break;
            case PICO_WARN_PU_DISCARD_BUF:
                base = PICOOS_MSG_WARN_PU_DISCARD_BUF;
                break;

            default:
                base = (picoos_char *) "unknown error";
                break;
        }
    }
    bsize = picoos_strlcpy(dst,base,siz);
    if ((NULL != fmt) && (bsize < siz)) { /* there is something to add and more space to add it */
        if (bsize > 0) {
            dst += bsize;
            siz -= bsize;
            bsize = picoos_strlcpy(dst,(picoos_char *)": ",siz);
        }
        if (bsize < siz) {
            picopal_vslprintf((picopal_char *) dst + bsize, siz - bsize, (picopal_char *)fmt, args);
        }
    }
}

void picoos_setErrorMsg(picoos_char * dst, picoos_objsize_t siz,
        picoos_int16 code, picoos_char * base, const picoos_char *fmt, ...)
{
    va_list args;
    va_start(args, (char *)fmt);
    picoos_vSetErrorMsg(dst,siz, code, base, fmt,args);
    va_end(args);
}

/* For convenience, this function returns the resulting current exception code. The return value therefore is NOT the status of raising
 * the error! */
pico_status_t picoos_emRaiseException(picoos_ExceptionManager this,
        pico_status_t exceptionCode, picoos_char * baseMessage, picoos_char * fmt, ...)
{
    va_list args;


    if (PICO_OK == this->curExceptionCode && PICO_OK != exceptionCode) {
        this->curExceptionCode = exceptionCode;
        va_start(args, (char *)fmt);
        picoos_vSetErrorMsg(this->curExceptionMessage,PICOOS_MAX_EXC_MSG_LEN, exceptionCode, baseMessage, fmt,args);
        PICODBG_DEBUG((
            "exit with exception code=%i, exception message='%s'",
            this->curExceptionCode, this->curExceptionMessage));

        va_end(args);

    }
    return this->curExceptionCode;
}

pico_status_t picoos_emGetExceptionCode(picoos_ExceptionManager this)
{
    return this->curExceptionCode;
}

void picoos_emGetExceptionMessage(picoos_ExceptionManager this, picoos_char * msg, picoos_uint16 maxsize)
{
        picoos_strlcpy(msg,this->curExceptionMessage,maxsize);
}

void picoos_emRaiseWarning(picoos_ExceptionManager this,
        pico_status_t warningCode, picoos_char * baseMessage, picoos_char * fmt, ...)
{
    va_list args;
    if ((this->curNumWarnings < PICOOS_MAX_NUM_WARNINGS) && (PICO_OK != warningCode)) {
        if (PICOOS_MAX_NUM_WARNINGS-1 == this->curNumWarnings) {
            this->curWarningCode[this->curNumWarnings] = PICO_EXC_MAX_NUM_EXCEED;
            picoos_strlcpy(this->curWarningMessage[this->curNumWarnings],(picoos_char *) "too many warnings",PICOOS_MAX_WARN_MSG_LEN);
        } else {
            this->curWarningCode[this->curNumWarnings] = warningCode;
            va_start(args, (char *)fmt);
            picoos_vSetErrorMsg(this->curWarningMessage[this->curNumWarnings],PICOOS_MAX_WARN_MSG_LEN, warningCode, baseMessage, fmt,args);
            va_end(args);
        }
        this->curNumWarnings++;
    }
    PICODBG_DEBUG((
        "exit with code=%i and message='%s', resulting in #warnings=%i",
        this->curWarningCode[this->curNumWarnings-1],
        this->curWarningMessage[this->curNumWarnings-1],
        this->curNumWarnings));
}

picoos_uint8 picoos_emGetNumOfWarnings(picoos_ExceptionManager this)
{
    return this->curNumWarnings;
}

pico_status_t picoos_emGetWarningCode(picoos_ExceptionManager this, picoos_uint8 index)
{
    if (index < this->curNumWarnings) {
      return this->curWarningCode[index];
    } else {
        return PICO_OK;
    }
}

void picoos_emGetWarningMessage(picoos_ExceptionManager this, picoos_uint8 index, picoos_char * msg, picoos_uint16 maxsize)
{
        if (index < this->curNumWarnings) {
            picoos_strlcpy(msg,this->curWarningMessage[index],maxsize);
        } else {
            msg[0] = NULLC;
        }
}




/* *****************************************************************/
/* File Access                                                     */
/* *****************************************************************/

#define picoos_MagicNumber 192837465
#define picoos_MaxBufSize 1000000
#define picoos_MaxNrOfBuffers 1000000
#define picoos_MaxBufLen 8192
#define picoos_HashFuncId0 0
#define picoos_HashTableSize0 101
#define picoos_HashTableSize1 1731
#define picoos_MaxHashTableSize HashTableSize1

#define cardinal_ptr_t picoos_uint32 *

typedef struct picoos_buffer
{
    picoos_char * data;
    int start; /* denotes the file position of the buffer beginning */
    int len; /* denotes the length of the buffer; -1 means invalid buffer */
    int pos; /* denotes the position in the buffer */
} picoos_buffer_t;

typedef picoos_buffer_t picoos_buffer_array_t[picoos_MaxNrOfBuffers];
typedef picoos_int32 picoos_buffer_index_array_t[picoos_MaxNrOfBuffers];

/**  object   : File
 *   shortcut : f
 *
 */
typedef struct picoos_file
{
    picoos_FileName name;
    picoos_uint8 binary;
    picoos_uint8 write;

    picopal_File nf;

    picoos_uint32 lFileLen;
    picoos_uint32 lPos;

    picoos_File next;
    picoos_File prev;

} picoos_file_t;

picoos_File picoos_newFile(picoos_MemoryManager mm)
{
    picoos_File this = (picoos_File) picoos_allocate(mm, sizeof(*this));
    if (NULL != this) {
        /* initialize */
    }
    return this;
}

void picoos_disposeFile(picoos_MemoryManager mm, picoos_File * this)
{
    if (NULL != (*this)) {
        /* terminate */
        picoos_deallocate(mm, (void *)this);
    }
}


/* ************************************************************
 * low-level file operations
 **************************************************************/

static picoos_int32 os_min(const picoos_int32 x, const picoos_int32 y)
{
    return (x < y) ? x : y;
}

/*
 static picoos_uint8 LReadChar (picoos_File f, picoos_char * ch);


 static picoos_uint8 LSetPos (picoos_File f, unsigned int pos);


 static picoos_uint8 LGetPos (picoos_File f, picoos_uint32 * pos);


 static picoos_uint8 LEof (picoos_File f);
 */

static picoos_bool LOpen(picoos_Common g, picoos_File * f,
        picoos_char fileName[], picopal_access_mode mode)
{
    picoos_bool done = TRUE;

    *f = picoos_newFile(g->mm);
    picopal_strcpy((*f)->name, fileName);
    (*f)->write = ((mode == PICOPAL_TEXT_WRITE) || (mode
            == PICOPAL_BINARY_WRITE));
    (*f)->binary = (mode
            == PICOPAL_BINARY_WRITE);
    (*f)->next = NULL;
    (*f)->prev = NULL;
    (*f)->nf = picopal_get_fnil();
    (*f)->lFileLen = 0;
    (*f)->lPos = 0;
    if (picopal_strlen((*f)->name)) {
       (*f)->nf = picopal_fopen((*f)->name, mode);
        done = !(picopal_is_fnil((*f)->nf));
        if (done) {
            (*f)->lFileLen = picopal_flength((*f)->nf);
        }
    }
    if (done) {
        (*f)->next = g->fileList;
        if (g->fileList != NULL) {
            g->fileList->prev = (*f);
        }
        g->fileList = (*f);
    } else {
        picoos_disposeFile(g->mm, f);
        (*f) = NULL;
    }
    return done;
}

static picoos_bool LClose(picoos_Common g, picoos_File * f)
{

    picoos_bool done;

    if (((*f) != NULL)) {
        done = (PICO_OK == picopal_fclose((*f)->nf));
        if (((*f)->next != NULL)) {
            (*f)->next->prev = (*f)->prev;
        }
        if (((*f)->prev != NULL)) {
            (*f)->prev->next = (*f)->next;
        } else {
            g->fileList = (*f)->next;
        }
        picoos_disposeFile(g->mm, f);

        done = TRUE;
    } else {
        done = FALSE;
    }
    return done;

}

/* caller must ensure that bytes[] has at least len allocated bytes */
static picoos_bool LReadBytes(picoos_File f, picoos_uint8 bytes[],
        picoos_uint32 * len)
{
    picoos_bool done;
    picoos_int32 res;

    PICODBG_TRACE(("trying to read %i bytes",*len));
    if ((f != NULL)) {
        res = picopal_fread_bytes(f->nf, (void *) &bytes[(0)], 1, (*len));
        PICODBG_TRACE(("res = %i",res));
        if (res < 0) { /* non-ansi */
            (*len) = 0;
            done = FALSE;
        } else if (((picoos_uint32)res != (*len))) {
            (*len) = res;
            done = FALSE;
        } else {
            done = TRUE;
        }
        f->lPos = (f->lPos + (*len));
    } else {
        (*len) = 0;
        done = FALSE;
    }
    return done;
}

 static picoos_bool LWriteBytes(picoos_File f, const picoos_char bytes[], int * len) {
    picoos_bool done;
    int res;
    /*int n;
    void * bptr; */

    if (f != NULL) {
        res = picopal_fwrite_bytes(f->nf, (void *) bytes, 1, *len);
        if ((res < 0)) {
            (*len) = 0;
            done = FALSE;
        } else if ((res != (*len))) {
            (*len) = res;
            done = FALSE;
        } else {
            done = TRUE;
        }
        f->lPos = (f->lPos + (unsigned int) (*len));
        if ((f->lPos > f->lFileLen)) {
            f->lFileLen = f->lPos;
        }
    } else {
        (*len) = 0;
        done = FALSE;
    }
    return done;
}


static picoos_bool LSetPos(picoos_File f, unsigned int pos)
{

    picoos_bool done;

    if ((f != NULL)) {
        if ((pos == f->lPos)) {
            done = TRUE;
        } else {
            done = (PICO_OK == picopal_fseek(f->nf, pos, PICOPAL_SEEK_SET));
            if (done) {
                f->lPos = pos;
            }
        }
    } else {
        done = FALSE;
    }
    return done;
}

static picoos_bool LGetPos(picoos_File f, picoos_uint32 * pos)
{
    picoos_bool done = TRUE;
    if ((f != NULL)) {
        (*pos) = f->lPos;
    } else {
        done = FALSE;
        (*pos) = 0;
    }
    return done;

}

static picoos_bool LEof(picoos_File f)
{
    picoos_bool isEof;

    if ((f != NULL)) {
        isEof = picopal_feof(f->nf);
    } else {
        isEof = TRUE;
    }
    return isEof;

}

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



/* read a given string 'str' from file. If no match was found, the read position is advanced until and including the first
 * non-matching character */
static picoos_bool picoos_StrRead (picoos_File f, picoos_char str[])
{
    picoos_uint32 i = 0;
    picoos_bool done = TRUE;
    picoos_char b;

    while (done && (str[i] != NULLC)) {
        done = done && picoos_ReadByte(f,(picoos_char *)&b);
        done = done && (b == str[i]);
        i++;
    }
    return done;
}

/* write 'str' to file */
static picoos_bool picoos_WriteStr (picoos_File f, picoos_char str[])
{
    picoos_uint32 i = 0;
    picoos_bool done = TRUE;

    while (done && (str[i] != NULLC)) {
        done = done && picoos_WriteByte(f,str[i]);
        i++;
    }
    return done;
}



/* **** Sequential binary file access ******/

/* Remark: 'ReadByte', 'ReadBytes' and 'ReadVar' may be mixed;
 'WriteByte', 'WriteBytes' and 'WriteVar' may be mixed. */

/* Open existing binary file for read access. Reading is buffered
 * with 'nrOfBufs' buffers of size 'bufSize'. If 'nrOfBufs' or
 * 'bufSize' is 0 reading is not buffered.
 * If 'key' is not empty, the file is decrypted with 'key'.
 * If the opened file is in an encrypted archive file, it
 */
picoos_uint8 picoos_OpenBinary(picoos_Common g, picoos_File * f,
        picoos_char fileName[])
{
    return LOpen(g, f, fileName, PICOPAL_BINARY_READ);
}


/* Read next byte from file 'f'. */
picoos_bool picoos_ReadByte(picoos_File f, picoos_uint8 * by)
{
    picoos_uint32 n = 1;

    return picoos_ReadBytes(f, by, &n) && (n == 1);

}

/* Read next 'len' bytes from 'f' into 'bytes'; 'len' returns the
 number of bytes actually read (may be smaller than requested
 length if at end of file). bytes[] must be big enough to hold at least len bytes.
*/
picoos_bool picoos_ReadBytes(picoos_File f, picoos_uint8 bytes[],
        picoos_uint32 * len)
{
    picoos_bool done = TRUE;
    /* unsigned int origPos; */

    if ((f != NULL)) {
        done = LReadBytes(f, bytes, len);
        /*if ((f->keyLen > 0)) {
         DecryptBytes(f->key,picoos_MaxKeyLen,f->keyLen,origPos,bytes,(*len));
         }*/
    }

    return done;
}


/* Create new binary file.
 If 'key' is not empty, the file is encrypted with 'key'. */
picoos_bool picoos_CreateBinary(picoos_Common g, picoos_File * f,
        picoos_char fileName[])
{
    return LOpen(g, f, fileName, PICOPAL_BINARY_WRITE);

}


picoos_uint8 picoos_WriteByte(picoos_File f, picoos_char by)
{
    int n = 1;

    return picoos_WriteBytes(f, (picoos_char *) &by, &n);
}


/* Writes 'len' bytes from 'bytes' onto file 'f'; 'len' returns
 the number of bytes actually written. */
picoos_bool picoos_WriteBytes(picoos_File f, const picoos_char bytes[],        picoos_int32 * len) {
    picoos_bool done = FALSE;

    if (f != NULL) {
        done = LWriteBytes(f, bytes, len);
    }

    return done;
}



/* Close previously opened binary file. */
picoos_uint8 picoos_CloseBinary(picoos_Common g, picoos_File * f)
{
    return LClose(g, f);

}

/* **************************************************************************************/
/* *** general routines *****/


/* Returns whether end of file was encountered in previous
 read operation. */
picoos_bool picoos_Eof(picoos_File f)
{
    if ((NULL != f)) {
        return LEof(f);
    } else {
        return TRUE;
    }

}

/* sets the file pointer to
 'pos' bytes from beginning (first byte = byte 0). This
 routine should only be used for binary files. */
picoos_bool picoos_SetPos(picoos_File f, picoos_int32 pos)
{
    picoos_bool done = TRUE;
    if ((NULL != f)) {
        done = LSetPos(f, pos);
    } else {
        done = FALSE;
    }
    return done;

}

/* Get position from file 'f'. */
picoos_bool picoos_GetPos(picoos_File f, picoos_uint32 * pos)
{
    if (NULL != f) {
        /* if (f->bFile) {
         (*pos) =  BGetPos(f);
         } else { */
        (*pos) =  LGetPos(f, pos);
        /* } */
        return TRUE;
    } else {
        (*pos) = 0;
        return FALSE;
    }
}

/* Returns the length of the file in bytes. */
picoos_bool picoos_FileLength(picoos_File f, picoos_uint32 * len)
{

    if (NULL != f) {
        *len = f->lFileLen;
        return TRUE;
    } else {
        *len = 0;
        return FALSE;
    }
}

/* Return full name of file 'f'. maxsize is the size of 'name[]' in bytes */
picoos_bool picoos_Name(picoos_File f, picoos_char name[], picoos_uint32 maxsize)
{
    picoos_bool done = TRUE;

    if (NULL != f) {
        done = (picoos_strlcpy(name, f->name,maxsize) < maxsize);
    } else {
        name[0] = (picoos_char)NULLC;
        done = FALSE;
    }

    return done;
}

/* Returns whether file 'name' exists or not. */
picoos_bool picoos_FileExists(picoos_Common g, picoos_char name[])
{
    picoos_File f;

    if (picoos_OpenBinary(g, & f,name)) {
        picoos_CloseBinary(g, & f);
        return TRUE;
    } else {
        return FALSE;
    }
}


/* ******************************************************************/
/* Array conversion operations: all procedures convert 'nrElems' values from
   'src' starting with index 'srcStartInd' into corresponding (possibly
   rounded) values in 'dst' starting at 'dstStartInd'. */

/* taking pi to be le, these are just the array versions of read_mem_pi_*int16 */
typedef picoos_uint8 two_byte_t[2];

static void arr_conv_le_int16 (picoos_uint8 src[], picoos_uint32 srcShortStartInd, picoos_uint32 nrElems, picoos_int16 dst[], picoos_uint32 dstStartInd)
{
    two_byte_t * src_p = (two_byte_t *) (src + (srcShortStartInd * 2));
    picoos_int16 * dst_p = dst + dstStartInd;
    picoos_uint32 i;

    for (i=0; i<nrElems; i++) {
        *(dst_p++) = (*src_p)[0] + (((*src_p)[1] & 0x7F) << 8) - (((*src_p)[1] & 0x80) ? 0x8000 : 0);
        src_p++;
    }
}



/* convert array of int16 into little-endian format */
static void arr_conv_int16_le (picoos_int16 src[], picoos_uint32 srcStartInd, picoos_uint32 nrElems, picoos_uint8 dst[], picoos_uint32 dstShortStartInd)
{
    two_byte_t * dst_p = (two_byte_t *) (dst + (dstShortStartInd * 2));
    picoos_int16 * src_p = src + srcStartInd;
    picoos_uint32 i;
    picoos_uint16 val;

    for (i=0; i<nrElems; i++) {
        val = (picoos_uint16) *(src_p++);
        (*dst_p)[0] = (picoos_uint8)(val & 0x00FF);
        (*dst_p)[1] = (picoos_uint8)((val & 0xFF00)>>8);
        dst_p++;
    }
}

/* *****************************************************************/
/* Sampled Data Files                                                    */
/* *****************************************************************/

#define PICOOS_SDF_BUF_LEN 1024

#define PICOOS_INT16_MIN   -32768
#define PICOOS_INT16_MAX   32767
#define PICOOS_UINT16_MAX  0xffff
#define PICOOS_INT32_MIN   -2147483648
#define PICOOS_INT32_MAX   2147483647
#define PICOOS_UINT32_MAX  0xffffffff

/**  object   : SDFile
 *   shortcut : sdf
 *
 */
/* typedef struct picoos_sd_file * picoos_SDFile */
typedef struct picoos_sd_file
{
    picoos_uint32 sf;
    wave_file_type_t fileType; /* (acoustic) wav, au, raw, other */
    picoos_uint32 hdrSize;
    picoos_encoding_t enc;
    picoos_File file;
    picoos_uint32 nrFileSamples;
    picoos_int16 buf[PICOOS_SDF_BUF_LEN];
    picoos_int32 bufPos;
    picoos_uint8 bBuf[2*PICOOS_SDF_BUF_LEN];
    picoos_bool aborted;
} picoos_sd_file_t;


/* Tries to read wav header at beginning of file 'f';
   returns sampling rate 'sf', encoding type 'enc',
   nr of samples in file 'nrSamples', header size 'hdrSize',
   and byte order 'bOrder'; returns whether a supported
   wav header and format was found. */
static picoos_bool picoos_readWavHeader(picoos_File f, picoos_uint32 * sf,
        picoos_encoding_t * enc, picoos_uint32 * nrSamples,
        picoos_uint32 * hdrSize) {
    picoos_uint16 n16;
    picoos_uint32 n32;
    picoos_uint16 formatTag;
    picoos_uint32 sampleRate;
    picoos_uint32 bytesPerSec;
    picoos_uint16 blockAlign;
    picoos_uint16 sampleSize;
    picoos_uint32 dataLength;
    picoos_uint32 fileLen;
    picoos_uint32 nrFileSamples;
    picoos_bool done;


    picoos_SetPos(f, 0);
    picoos_FileLength(f, &fileLen);
    done = picoos_StrRead(f, (picoos_char *) "RIFF");
    done = done && (PICO_OK == picoos_read_le_uint32(f, &n32)); /* length of riff chunk, unused */
    done = done && picoos_StrRead(f, (picoos_char *) "WAVE");
    done = done && picoos_StrRead(f, (picoos_char *) "fmt ");
    done = done && (PICO_OK == picoos_read_le_uint32(f, &n32)); /* length of fmt chunk in bytes; must be 16 */
    done = done && (n32 == 16);
    done = done && (PICO_OK == picoos_read_le_uint16(f, &formatTag));
    done = done && (PICO_OK == picoos_read_le_uint16(f, &n16)); /* number of channels; must be mono */
    done = done && (n16 == 1);
    done = done && (PICO_OK == picoos_read_le_uint32(f, &sampleRate));
    done = done && (PICO_OK == picoos_read_le_uint32(f, &bytesPerSec));
    done = done && (PICO_OK == picoos_read_le_uint16(f, &blockAlign));
    done = done && (PICO_OK == picoos_read_le_uint16(f, &sampleSize));
    done = done && picoos_StrRead(f, (picoos_char *) "data");
    done = done && (PICO_OK == picoos_read_le_uint32(f, &dataLength)); /* length of data chunk in bytes */
    (*hdrSize) = 44;
    if (done) {
        (*sf) = sampleRate;
        (*nrSamples) = 0;
        switch (formatTag) {
        case FORMAT_TAG_LIN:
            (*enc) = PICOOS_ENC_LIN;
            done = ((blockAlign == 2) && (sampleSize == 16));
            (*nrSamples) = (dataLength / 2);
            nrFileSamples = ((fileLen - (*hdrSize)) / 2);
            break;
        case FORMAT_TAG_ULAW:
            (*enc) = PICOOS_ENC_ULAW;
            done = ((blockAlign == 1) && (sampleSize == 8));
            (*nrSamples) = dataLength;
            nrFileSamples = (fileLen - (*hdrSize));
            break;
        case FORMAT_TAG_ALAW:
            (*enc) = PICOOS_ENC_ALAW;
            done = ((blockAlign == 1) && (sampleSize == 8));
            (*nrSamples) = dataLength;
            nrFileSamples = (fileLen - (*hdrSize));
            break;
        default:
            done = FALSE;
            break;
        }
        if (!done) {
            /* communicate "unsupported format" */
            PICODBG_WARN(("unsupported wav format"));
        } else {
            if (nrFileSamples != (*nrSamples)) {
                /* warn "inconsistent number of samples" */
                PICODBG_WARN(("inconsistent number of samples in wav file: %d vs. %d",nrFileSamples,(*nrSamples)));
                (*nrSamples) = nrFileSamples;
            }
        }
    }
    return done;
}



extern picoos_bool picoos_sdfOpenIn(picoos_Common g, picoos_SDFile * sdFile,
        picoos_char fileName[], picoos_uint32 * sf, picoos_encoding_t * enc,
        picoos_uint32 * numSamples)
{
    picoos_bool done = FALSE;
    picoos_sd_file_t * sdf = NULL;
    wave_file_type_t fileType = FILE_TYPE_OTHER;

    (*sf) = 0;
    (*numSamples) = 0;
    (*enc) = PICOOS_ENC_LIN;
    (*sdFile) = NULL;

    sdf = picoos_allocate(g->mm,sizeof(picoos_sd_file_t));
    if (NULL == sdf) {
        picoos_emRaiseWarning(g->em,PICO_EXC_OUT_OF_MEM,NULL,NULL);
        return FALSE;
    }

    /* buffered access not supported, yet */
    if (picoos_OpenBinary(g,&(sdf->file),fileName)) {
        if (picoos_has_extension(fileName,(picoos_char *) ".wav")) {
            fileType = FILE_TYPE_WAV;
            done = picoos_readWavHeader(sdf->file,&(sdf->sf),&(sdf->enc),&(sdf->nrFileSamples),&(sdf->hdrSize));
        } else {
            /* we prefer not to treat other formats, rather than treat it as raw */
            /* fileType = FILE_TYPE_RAW; */
            fileType = FILE_TYPE_OTHER;
            done = FALSE;
        }

        if (FILE_TYPE_OTHER == fileType) {
            picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,(picoos_char *)"unsupported filename suffix",NULL);
        } else if (!done) {
            picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,(picoos_char *)"non-conforming header",NULL);
        } else {
            (*numSamples) = sdf->nrFileSamples;
            (*sf) = sdf->sf;
            (*enc) = sdf->enc;
            /* check whether sd file properties are supported */
            if (PICOOS_ENC_LIN != sdf->enc) {
                done = FALSE;
                picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,NULL,(picoos_char *)"encoding not supported");
            }
            if (SAMPLE_FREQ_16KHZ != sdf->sf) {
                done = FALSE;
                picoos_emRaiseWarning(g->em,PICO_EXC_UNEXPECTED_FILE_TYPE,NULL,(picoos_char *)"sample frequency not supported");
            }
            (*sdFile) = sdf;
        }
        if (!done){
            picoos_CloseBinary(g,&(sdf->file));
        }
    } else {
        picoos_emRaiseException(g->em,PICO_EXC_CANT_OPEN_FILE,NULL,NULL);
    }
    if (!done) {
        picoos_deallocate(g->mm,(void *)&sdf);
        (*sdFile) = NULL;
    }
    return done;
}


static void picoos_sdfLoadSamples(picoos_SDFile sdFile,
        picoos_uint32 * nrSamples) {
    picoos_uint32 len;
    picoos_sd_file_t * sdf = sdFile;

    switch (sdFile->enc) {
    case PICOOS_ENC_LIN:
        if ((*nrSamples) > PICOOS_SDF_BUF_LEN) {
            (*nrSamples) = PICOOS_SDF_BUF_LEN;
        }
        len = 2 * (*nrSamples);
        picoos_ReadBytes(sdf->file, sdf->bBuf, &len);
        (*nrSamples) = len / 2;
        arr_conv_le_int16(sdf->bBuf, 0, (*nrSamples), sdf->buf, 0);
        break;
        /* @todo : may be useful */
    case PICOOS_ENC_ULAW:
    case PICOOS_ENC_ALAW:
    default:
        (*nrSamples) = 0;
    }

}

extern picoos_bool picoos_sdfGetSamples (
        picoos_SDFile sdFile,
        picoos_uint32 start,
        picoos_uint32 * nrSamples,
        picoos_int16 samples[])
{
    picoos_uint32 b;
    picoos_uint32 rem;
    picoos_uint32 n;
    picoos_uint32 i;
    picoos_uint32 j;
    picoos_bool done = FALSE;

    if (NULL == sdFile) {
        (*nrSamples) = 0;
    } else {
            if (start >= sdFile->nrFileSamples) {
                if (start > sdFile->nrFileSamples) {
                    PICODBG_WARN(("start has to be <= sdFile->nrFileSamples"));
                }
                (*nrSamples) = 0;
            } else {
                if (((start + (*nrSamples)) > sdFile->nrFileSamples)) {
                    (*nrSamples) = (sdFile->nrFileSamples - start);
                }
                if ((sdFile->enc == PICOOS_ENC_LIN)) {
                    b = 2;
                } else {
                    b = 1;
                }
                picoos_SetPos(sdFile->file,(sdFile->hdrSize + (b * start)));
                j = 0;
                rem = (*nrSamples);
                n = rem;
                while ((rem > 0) && (n > 0)) {
                    /* set n=min(rem,buffer_length) and try loading next n samples */
                    n = (rem < PICOOS_SDF_BUF_LEN) ? rem : PICOOS_SDF_BUF_LEN;
                    picoos_sdfLoadSamples(sdFile, &n);
                    /* n may be smaller now */
                    for (i = 0; i < n; i++) {
                        samples[j] = sdFile->buf[i];
                        j++;
                    }
                    rem -= n;
                    start += n;
                }
                (*nrSamples) = j;
                done = ((*nrSamples) > 0);
            }
    }
    return done;
}


extern picoos_bool picoos_sdfCloseIn (picoos_Common g, picoos_SDFile * sdFile)
{
    if (NULL != (*sdFile)) {
        picoos_CloseBinary(g,&((*sdFile)->file));
        picoos_deallocate(g->mm,(void *)sdFile);
    }
    return TRUE;
}


static picoos_bool picoos_writeWavHeader(picoos_File f, picoos_uint32 sf,
        picoos_encoding_t enc, picoos_uint32 nrSamples,
        picoos_uint32 * hdrSize) {
    picoos_uint16 formatTag = FORMAT_TAG_LIN;
    picoos_uint32 sampleRate;
    picoos_uint32 bytesPerSec;
    picoos_uint32 bytesPerSample = 2;
    picoos_uint16 blockAlign;
    picoos_uint16 sampleSize = 16;
    picoos_uint32 dataLength;
    picoos_bool done = TRUE;

    picoos_SetPos(f, 0);

    switch (enc) {
        case PICOOS_ENC_LIN:
            formatTag = FORMAT_TAG_LIN;
            bytesPerSample = 2;
            sampleSize = 16;
            break;
        case PICOOS_ENC_ULAW:
            formatTag = FORMAT_TAG_ULAW;
            bytesPerSample = 1;
            sampleSize = 8;
            break;
        case PICOOS_ENC_ALAW:
            formatTag = FORMAT_TAG_ALAW;
            bytesPerSample = 1;
            sampleSize = 8;
            break;
        default:
            done = FALSE;
            break;
    }

    bytesPerSec = (sf * bytesPerSample);
    blockAlign = bytesPerSample;
    sampleRate = sf;
    dataLength = (bytesPerSample * nrSamples);
    done = done && picoos_WriteStr(f,(picoos_char *)"RIFF");
    done = done && picoos_write_le_uint32(f,dataLength + 36);
    done = done && picoos_WriteStr(f,(picoos_char *)"WAVE");
    done = done && picoos_WriteStr(f,(picoos_char *)"fmt ");
    done = done && picoos_write_le_uint32(f,16);
    done = done && picoos_write_le_uint16(f,formatTag);
    done = done && picoos_write_le_uint16(f,1);
    done = done && picoos_write_le_uint32(f,sampleRate);
    done = done && picoos_write_le_uint32(f,bytesPerSec);
    done = done && picoos_write_le_uint16(f,blockAlign);
    done = done && picoos_write_le_uint16(f,sampleSize);
    done = done && picoos_WriteStr(f,(picoos_char *)"data");
    done = done && picoos_write_le_uint32(f,dataLength);
    (*hdrSize) = 44;
    return done;
}


#define DummyLen 100000000

extern picoos_bool picoos_sdfOpenOut(picoos_Common g, picoos_SDFile * sdFile,
        picoos_char fileName[], int sf, picoos_encoding_t enc)
{
    picoos_bool done = TRUE;
    picoos_sd_file_t * sdf = NULL;

    (*sdFile) = NULL;
    sdf = picoos_allocate(g->mm, sizeof(picoos_sd_file_t));
    if (NULL == sdf) {
        picoos_emRaiseWarning(g->em, PICO_EXC_OUT_OF_MEM, NULL, NULL);
        return FALSE;
    }
    sdf->sf = sf;
    sdf->enc = enc;
    /* check whether sd file properties are supported */
    if (PICOOS_ENC_LIN != sdf->enc) {
        done = FALSE;
        picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE, NULL,
                (picoos_char *) "encoding not supported");
    }
    if (SAMPLE_FREQ_16KHZ != sdf->sf) {
        done = FALSE;
        picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE, NULL,
                (picoos_char *) "sample frequency not supported");
    }
    if (done) {
        sdf->nrFileSamples = 0;
        sdf->bufPos = 0;
        sdf->aborted = FALSE;
        if (picoos_CreateBinary(g, &(sdf->file), fileName)) {
            if (picoos_has_extension(fileName, (picoos_char *) ".wav")) {
                sdf->fileType = FILE_TYPE_WAV;
                done = picoos_writeWavHeader(sdf->file, sdf->sf, sdf->enc,
                        DummyLen, &(sdf->hdrSize));
            } else {
                /* we prefer not to treat other formats, rather than treat it as raw */
                /* fileType = FILE_TYPE_RAW; */
                sdf->fileType = FILE_TYPE_OTHER;
                done = FALSE;
            }

            if (FILE_TYPE_OTHER == sdf->fileType) {
                picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE,
                        (picoos_char *) "unsupported filename suffix", NULL);
            } else if (!done) {
                picoos_emRaiseWarning(g->em, PICO_EXC_UNEXPECTED_FILE_TYPE,
                        (picoos_char *) "non-conforming header", NULL);
            } else {
                (*sdFile) = sdf;
            }
            if (!done) {
                picoos_CloseBinary(g, &(sdf->file));
            }
        } else {
            picoos_emRaiseException(g->em, PICO_EXC_CANT_OPEN_FILE, NULL, NULL);
        }
    }
    if (!done) {
        picoos_deallocate(g->mm, (void *) &sdf);
        (*sdFile) = NULL;
    }
    return done;
}

static picoos_bool picoos_sdfFlushOutBuf(picoos_SDFile sdFile)
{
    picoos_bool done = FALSE;
    picoos_int32 len;
    picoos_int32 nrSamples;

    if (!(sdFile->aborted)) {
        nrSamples = sdFile->bufPos;
        switch (sdFile->enc) {
            case PICOOS_ENC_LIN:
                arr_conv_int16_le(sdFile->buf, 0, nrSamples, sdFile->bBuf, 0);
                len = (nrSamples * 2);
                done = picoos_WriteBytes(sdFile->file, sdFile->bBuf, &len)
                        && ((nrSamples * 2) == len);
                break;
            case PICOOS_ENC_ULAW:
            case PICOOS_ENC_ALAW:
            default:
                nrSamples = 0;
                break;
        }
        sdFile->nrFileSamples = (sdFile->nrFileSamples + nrSamples);
    }

    sdFile->bufPos = 0;
    return done;
}

extern picoos_bool picoos_sdfFlushOutput(picoos_SDFile sdFile)
{
    if ((sdFile != NULL) &&  !(sdFile->aborted) && (sdFile->bufPos > 0)) {
        return picoos_sdfFlushOutBuf(sdFile);
    }
    return TRUE;
}



extern picoos_bool picoos_sdfPutSamples (picoos_SDFile sdFile, picoos_uint32 nrSamples, picoos_int16 samples[])
{
    picoos_uint32 i;
    picoos_int32 s;
    picoos_bool done = FALSE;

    if ((sdFile != NULL) &&  !(sdFile->aborted)) {
        done = TRUE;
        for (i = 0; i < nrSamples; i++) {
            s = samples[i];
            if ((s > PICOOS_INT16_MAX)) {
                s = PICOOS_INT16_MAX;
            } else if (s < PICOOS_INT16_MIN) {
                s = PICOOS_INT16_MIN;
            }
            sdFile->buf[sdFile->bufPos++] = s;
            if (sdFile->bufPos >= PICOOS_SDF_BUF_LEN) {
                done = picoos_sdfFlushOutBuf(sdFile);
            }
        }
    } else {
        done = FALSE;
    }
    return done;
}


extern picoos_bool picoos_sdfCloseOut (picoos_Common g, picoos_SDFile * sdFile)
{

    picoos_bool done = TRUE;
    picoos_uint32 hdrSize;

    if (NULL != (*sdFile)) {
        if (!((*sdFile)->aborted) && ((*sdFile)->bufPos > 0)) {
            done = picoos_sdfFlushOutBuf(*sdFile);
        }
        if (FILE_TYPE_WAV == (*sdFile)->fileType) {
            done = picoos_writeWavHeader((*sdFile)->file, (*sdFile)->sf,
                    (*sdFile)->enc, (*sdFile)->nrFileSamples, &hdrSize);
        }
        done = picoos_CloseBinary(g, &((*sdFile)->file));
        picoos_deallocate(g->mm, (void *) sdFile);
    }
    return done;
}


/* *****************************************************************/
/* FileHeader                                                      */
/* *****************************************************************/



pico_status_t picoos_clearHeader(picoos_FileHeader header)
{
    picoos_uint8 i;
    for (i=0; i < PICOOS_MAX_NUM_HEADER_FIELDS; i++) {
        header->field[i].key[0] = NULLC;
        header->field[i].value[0] = NULLC;
        header->field[i].op = PICOOS_FIELD_IGNORE;
    }
    header->numFields = 0;
    return PICO_OK;
}

pico_status_t picoos_setHeaderField(picoos_FileHeader header,
        picoos_uint8 index, picoos_char * key, picoos_char * value,
        picoos_compare_op_t op)
{
    if (index >= header->numFields) {
        return PICO_ERR_INDEX_OUT_OF_RANGE;
    }
    header->field[index].op = op;
    if ((picoos_strlcpy(header->field[index].key, key,
            PICOOS_MAX_FIELD_STRING_LEN) < PICOOS_MAX_FIELD_STRING_LEN)
            && (picoos_strlcpy(header->field[index].value, value,
                    PICOOS_MAX_FIELD_STRING_LEN)) < PICOOS_MAX_FIELD_STRING_LEN) {
        return PICO_OK;
    } else {
        return PICO_ERR_INDEX_OUT_OF_RANGE;
    }
}


/* caller has to make sure allocated space at key and value are large enough to hold a picoos_field_string */
pico_status_t picoos_getHeaderField(picoos_FileHeader header, picoos_uint8 index, picoos_field_string_t key, picoos_field_string_t value, picoos_compare_op_t * op)
{
    if (index >= header->numFields) {
        return PICO_ERR_INDEX_OUT_OF_RANGE;
    }
    *op = header->field[index].op;
    if ((picoos_strlcpy(key,header->field[index].key,
            PICOOS_MAX_FIELD_STRING_LEN) < PICOOS_MAX_FIELD_STRING_LEN)
            && (picoos_strlcpy(value,header->field[index].value,
                    PICOOS_MAX_FIELD_STRING_LEN)) < PICOOS_MAX_FIELD_STRING_LEN) {
        return PICO_OK;
    } else {
        return PICO_ERR_INDEX_OUT_OF_RANGE;
    }
    return PICO_OK;
}


/* check whether 'str' of length strlen matches contents in circular buffer buf, located in the first strlen bytes and ending
 * position bufpos. */
static picoos_uint8 os_matched( picoos_char * str,  picoos_uint32 strlen, picoos_char * buf,  picoos_int32 bufpos) {
    picoos_int32 i = strlen-1;
    while (i >= 0 && buf[bufpos] == str[i]) {
        i--;
        bufpos--;
        if (bufpos < 0) {
            bufpos = strlen-1;
        }
    }
    return (i<0);
}

pico_status_t picoos_getSVOXHeaderString(picoos_char * str, picoos_uint8 * len, picoos_uint32 maxlen)
{
    picoos_char * ch;
    *len = picoos_strlcpy(str,picoos_SVOXFileHeader,maxlen);
    if (*len < maxlen) {
        ch = str;
        /* SVOX header is made less readable */
        while (*ch) {
            *ch -= ' ';
            ch++;
        }
        return PICO_OK;
    } else {
        return PICO_ERR_OTHER;
    }
}

pico_status_t picoos_readPicoHeader(picoos_File f, picoos_uint32 * headerlen)
{
    picoos_char str[32];
    picoos_char buf[32];
    picoos_uint8 strlen, bufpos;
    picoos_uint32 n;
    picoos_uint8 done;

    picoos_getSVOXHeaderString(str,&strlen,32);
    /* search for svox header somewhere near the file start. This allows for initial
     * non-svox-header bytes for a customer-specific header and/or filling bytes for alignment */
    *headerlen = 0;
    /* read in initial chunk of length strlen */
    n = strlen;
    done = picoos_ReadBytes(f,(picoos_uint8 *)buf,&n) && (n == strlen);
    if (done) {
        *headerlen = n;
        bufpos = strlen-1; /* last legal buf position */
        done = os_matched(str,strlen,buf,bufpos);
        while (!done && *headerlen < PICO_MAX_FOREIGN_HEADER_LEN) {
            n = 1;
            bufpos = (bufpos + 1) % strlen;
            done = picoos_ReadBytes(f,(picoos_uint8 *)buf+bufpos,&n) && 1 == n;
            done = done && os_matched(str,strlen,buf,bufpos);
            headerlen++;
        }
    }
    if (done) {
        return PICO_OK;
    } else {
        return PICO_EXC_UNEXPECTED_FILE_TYPE;
    }
}

picoos_uint8 picoos_get_str (picoos_char * fromStr, picoos_uint32 * pos, picoos_char * toStr, picoos_objsize_t maxsize)
{
    picoos_uint8 i = 0;
    /* skip non-printables */

    while ((fromStr[*pos] != NULLC) && (fromStr[*pos] <= ' ')) {
        (*pos)++;
    }
    /* copy printable portion */
    while ((fromStr[*pos] != NULLC) && (fromStr[*pos] > ' ') && (i < maxsize-1)) {
        toStr[i++] = fromStr[(*pos)++];
    }
    toStr[i] = NULLC;
    return (i > 0) && (fromStr[*pos] <= ' ');
}

pico_status_t picoos_hdrParseHeader(picoos_FileHeader header, picoos_header_string_t str)
{
    picoos_uint32 curpos = 0;
    picoos_uint8 i, numFields;


    /* read number of fields */
    numFields = str[curpos++];
    numFields = os_min(numFields,PICOOS_MAX_NUM_HEADER_FIELDS);
    /* read in all field pairs */
    PICODBG_DEBUG(("number of fields = %i", numFields));
    for (i = 0; i < numFields; i++) {
        picoos_get_str(str,&curpos,header->field[i].key,PICOOS_MAX_FIELD_STRING_LEN);
        picoos_get_str(str,&curpos,header->field[i].value,PICOOS_MAX_FIELD_STRING_LEN);
    }
    return PICO_OK;
}





/* **************************************************************************/
/* Read  little-endian / platform-independent integers from file or memory  */
/* **************************************************************************/

/* read little-endian */
pico_status_t picoos_read_le_uint16 (picoos_File file, picoos_uint16 * val)
{
    picoos_uint8 by[2];
    picoos_uint32 n = 2;
    if (picoos_ReadBytes(file, by, &n) && 2 == n) {
        /* little-endian */
        *val = (picoos_uint16) by[1] << 8 | (picoos_uint16) by[0];
        return PICO_OK;
    } else {
        *val = 0;
        return PICO_ERR_OTHER;
    }
}

pico_status_t picoos_read_le_int16 (picoos_File file, picoos_int16 * val)
{
    return picoos_read_le_uint16(file, (picoos_uint16 *)val);
}

pico_status_t picoos_read_le_uint32 (picoos_File file, picoos_uint32 * val)
{
    picoos_uint8 by[4];
    picoos_uint32 n = 4;
    if (picoos_ReadBytes(file, by, &n) && (4 == n)) {
        /* little-endian */
        PICODBG_TRACE(("reading uint 32:  %i %i %i %i",
                       by[0], by[1], by[2], by[3]));
        *val = (((picoos_uint32) by[3] << 8 | (picoos_uint32) by[2]) << 8 | (picoos_uint32) by[1]) << 8 | (picoos_uint32) by[0];
        PICODBG_TRACE(("uint 32:  %i %i %i %i corresponds %i",
                       by[0], by[1], by[2], by[3], *val));
        return PICO_OK;
    } else {
        *val = 0;
        return PICO_ERR_OTHER;
    }
}

/* platform-independent */
/* our convention is that pi is little-endian. */

/** @todo : direct implementation if too slow */

pico_status_t picoos_read_pi_uint16 (picoos_File file, picoos_uint16 * val)
{
    return picoos_read_le_uint16(file,val);
}

pico_status_t picoos_read_pi_uint32 (picoos_File file, picoos_uint32 * val)
{
    return picoos_read_le_uint32(file, val);
}

pico_status_t picoos_read_pi_int32 (picoos_File file, picoos_int32 * val)
{
    return picoos_read_le_uint32(file, (picoos_uint32 *)val);
}

/* read pi from memory */

pico_status_t picoos_read_mem_pi_uint16 (picoos_uint8 * data, picoos_uint32 * pos, picoos_uint16 * val)
{
    picoos_uint8 * by = data + *pos;

    /* little-endian */
    *val = (picoos_uint16) by[1] << 8 | (picoos_uint16) by[0];
    (*pos) += 2;
    return PICO_OK;
}

pico_status_t picoos_read_mem_pi_uint32 (picoos_uint8 * data, picoos_uint32 * pos, picoos_uint32 * val)
{
        picoos_uint8 * by = data + *pos;

        /* little-endian */
        *val = (((picoos_uint32) by[3] << 8 | (picoos_uint32) by[2]) << 8 | (picoos_uint32) by[1]) << 8 | (picoos_uint32) by[0];
        (*pos) += 4;
        return PICO_OK;
}

/* **************************************************************************/
/* Write little-endian / platform-independent integers into file or memory  */
/* **************************************************************************/
/* write little-endian */
pico_status_t picoos_write_le_uint16 (picoos_File file, picoos_uint16 val)
{
    picoos_int32 len = 2;
    picoos_uint8 by[2];

    by[0]  = (picoos_uint8)((val) & 0x00FF);
    by[1]  = (picoos_uint8)(((val) & 0xFF00)>>8);
    return (picoos_WriteBytes(file,by,&len) && (2 == len));
}
pico_status_t picoos_write_le_uint32 (picoos_File file, picoos_uint32 val)
{
    picoos_int32 len = 4;
    picoos_uint8 by[4];

    by[0]  = (picoos_uint8)(val & 0x000000FF);
    by[1]  = (picoos_uint8)((val & 0x0000FF00)>>8);
    by[2]  = (picoos_uint8)((val & 0x00FF0000)>>16);
    by[3]  = (picoos_uint8)((val & 0xFF000000)>>24);
    return (picoos_WriteBytes(file,by,&len) && (4 == len));
}

/* write pi to mem */
pico_status_t picoos_write_mem_pi_uint16 (picoos_uint8 * data, picoos_uint32 * pos, picoos_uint16 val)
{
    picoos_uint8 * by = data + *pos;
    /* little-endian */
    by[0]  = (picoos_uint8)((val) & 0x00FF);
    by[1]  = (picoos_uint8)(((val) & 0xFF00)>>8);
    (*pos) += 2;
    return PICO_OK;
}

/* *****************************************************************/
/* String search and compare operations                            */
/* *****************************************************************/

/* this function is case-sensitive */
picoos_uint8 picoos_has_extension(const picoos_char *str, const picoos_char *suf)
{
    picoos_int32 istr = picoos_strlen(str)-1;
    picoos_int32 isuf = picoos_strlen(suf)-1;
    while ((istr >= 0) && (isuf >=0) && (str[istr] == suf[isuf])) {
        istr--;
        isuf--;
    }
    return (isuf < 0);
}


/* *****************************************************************/
/* String/Number Conversions  (may be moved to picopal)            */
/* *****************************************************************/

pico_status_t picoos_string_to_int32(picoos_char str[],
        picoos_int32 * res)
{
    /* syntax: [+|-] dig {dig} */

    int i;
    int neg;
    int val;
    int err;

    err = 0;
    i = 0;
    while ((str[i] <= ' ') && (str[i] != '\0')) {
        i++;
    }
    neg = 0;
    if (str[i] == '-') {
        neg = 1;
        i++;
    } else if (str[i] == '+') {
        i++;
    }
    val = 0;
    if ((str[i] < '0') || (str[i]> '9')) {
        err = 1;
    }
    while ((str[i] >= '0') && (str[i] <= '9')) {
        val = val * 10 + (str[i] - '0');
        i++;
    }
    while ((str[i] <= ' ') && (str[i] != '\0')) {
        i++;
    }
    if (neg == 1) {
        val = -val;
    }
    if ((err == 0) && (str[i] == '\0')) {
        (*res) = val;
        return PICO_OK;
    } else {
        (*res) = 0;
        return PICO_EXC_NUMBER_FORMAT;
    }
}

pico_status_t picoos_string_to_uint32(picoos_char str[],
        picoos_uint32 * res)
{
    /* syntax: [+] dig {dig} */

    int i;
    int val;
    int err;

    err = 0;
    i = 0;
    while ((str[i] <= ' ') && (str[i] != '\0')) {
        i++;
    }
    if (str[i] == '+') {
        i++;
    }
    val = 0;
    if ((str[i] < '0') || (str[i]> '9')) {
        err = 1;
    }
    while ((str[i] >= '0') && (str[i] <= '9')) {
        val = val * 10 + (str[i] - '0');
        i++;
    }
    while ((str[i] <= ' ') && (str[i] != '\0')) {
        i++;
    }
    if ((err == 0) && (str[i] == '\0')) {
        (*res) = val;
        return PICO_OK;
    } else {
        (*res) = 0;
        return PICO_EXC_NUMBER_FORMAT;
    }
}

/* 'stringlen' is the part of input string to be considered,
 * possibly not containing NULLC (e.g. result of strlen).
 * 'maxsize' is the maximal size of 'part' including a byte
 * for the terminating NULLC! */
void picoos_get_sep_part_str(picoos_char string[],
        picoos_int32 stringlen, picoos_int32 * ind, picoos_char sepCh,
        picoos_char part[], picoos_int32 maxsize, picoos_uint8 * done)
{

    picoos_int32 j;
    picoos_uint8 done1;

    if (((*ind) >= stringlen)) {
        (*done) = 0;
        part[0] = (picoos_char) NULLC;
    } else {
        done1 = 1;
        j = 0;
        while ((((*ind) < stringlen) && (string[(*ind)] != sepCh)) && (string[((*ind))] != (picoos_char)NULLC)) {
            if ((j < maxsize-1)) {
                part[(j)] = string[(*ind)];
                j++;
            } else {
                done1 = 0;
            }
            (*ind)++;
        }
        part[j] = (picoos_char)NULLC;
        if ((*ind) < stringlen) {
            if ((string[(*ind)] == sepCh)) {
                (*ind)++; /* skip separator character */
            } else if (string[(*ind)] == (picoos_char)NULLC) {
                /* reached end of input; set ind to stringlen so that no
                 more (empty) partial strings will be found */
                (*ind) = stringlen;
            }
        }
        (*done) = done1;
    }
}

/* *****************************************************************/
/* timer function                                                  */
/* *****************************************************************/

extern void picoos_get_timer(picopal_uint32 * sec, picopal_uint32 * usec)
{
    picopal_get_timer(sec, usec);
}

#ifdef __cplusplus
}
#endif


/* end */
