/* SPDX-License-Identifier: MIT. RPMC-26 memory song serialization. */
#include "rpmc.h"
#include <stdlib.h>
#include <string.h>
#define LIMIT ((size_t)67108864)
typedef struct {const uint8_t *s;size_t n,p;int error;} reader;
typedef struct {uint8_t *s;size_t n,capacity;int error;} writer;
static uint32_t u32(const uint8_t *s){return s[0]|(uint32_t)s[1]<<8|(uint32_t)s[2]<<16|(uint32_t)s[3]<<24;}
static const uint8_t *take(reader *r,size_t n){const uint8_t *p;if(r->error||r->p>r->n||n>r->n-r->p){r->error=1;return NULL;}p=r->s+r->p;r->p+=n;return p;}
static uint32_t get(reader *r,unsigned n){const uint8_t *p=take(r,n);return !p?0:n==2?p[0]|(uint32_t)p[1]<<8:u32(p);}
static rpmc_bytes data(reader *r,size_t n){rpmc_bytes b={NULL,0};const uint8_t *p=take(r,n);if(!p)return b;b.bytes=malloc(n+1);if(!b.bytes){r->error=1;return b;}memcpy(b.bytes,p,n);b.bytes[n]=0;b.size=n;return b;}
static rpmc_bytes string(reader *r){size_t n=get(r,4);return data(r,n);}
static void write(writer *w,const uint8_t *s,size_t n){uint8_t *p;size_t c;if(w->error||n>LIMIT-w->n||(n&&!s)){w->error=1;return;}if(n>w->capacity-w->n){c=w->capacity?w->capacity:1024;while(c<w->n+n)c=c>LIMIT/2?LIMIT:c*2;p=realloc(w->s,c);if(!p){w->error=1;return;}w->s=p;w->capacity=c;}if(n)memcpy(w->s+w->n,s,n);w->n+=n;}
static void put(writer *w,uint32_t v,unsigned n){uint8_t p[4];unsigned i;for(i=0;i<n;i++)p[i]=(uint8_t)(v>>(8*i));write(w,p,n);}
static void put_string(writer *w,rpmc_bytes b){if(b.size>LIMIT){w->error=1;return;}put(w,(uint32_t)b.size,4);write(w,b.bytes,b.size);}
static int valid(const rpmc_song *s){size_t i,j,events=0;uint8_t seen[256];
    if(!s||s->length<1||s->length>2048||s->channels<1||s->channels>64||s->patterns<1||s->patterns>2048||s->speed<1||s->speed>255||s->bpm<1||s->bpm>999||s->global_volume>128||s->mix_volume>128||s->pan_separation>256||s->restart>=s->length||s->sample_count>128||s->instrument_count>256||s->xm_instrument_count>128||!s->order||!s->order_rows||!s->pattern_rows||!s->channel_state||s->sample_data.size>LIMIT||s->source_data.size>LIMIT)return -1;
    for(i=0;i<s->length;i++)if(s->order[i]>=s->patterns||s->order_rows[i]>4096||s->order_rows[i]>s->pattern_rows[s->order[i]])return -1;
    for(i=0;i<s->channels;i++)if(s->channel_state[i].pan>256||s->channel_state[i].volume>64||s->channel_state[i].muted>1||s->channel_state[i].surround>1)return -1;
    for(i=0;i<s->patterns;i++){if(!s->pattern_rows[i]||s->pattern_rows[i]>4096)return -1;events+=(size_t)s->pattern_rows[i]*s->channels*44;}if(events!=s->events.size||events>LIMIT||!s->events.bytes)return -1;
    if((s->sample_count&&!s->samples)||(s->instrument_count&&!s->instruments)||(s->xm_instrument_count&&!s->xm_instruments))return -1;
    for(j=0;j<2;j++){const rpmc_meta *list=j?s->instruments:s->samples;size_t count=j?s->instrument_count:s->sample_count;memset(seen,0,sizeof(seen));for(i=0;i<count;i++){const uint8_t *m=list[i].meta;size_t length=u32(m+4),channels=u32(m+1395),offset=u32(m+1387),right=u32(m+1391),k;if(list[i].index>=(j?256:128)||seen[list[i].index]++)return -1;if(u32(m+44)>1)return -1;if(!j&&(channels>2||u32(m+20)>64||u32(m+24)>64||length>s->sample_data.size||(length&&(offset>s->sample_data.size-length||(channels==2&&right>s->sample_data.size-length)))))return -1;if(j)for(k=0;k<240;k++)if(list[i].keymap[k]>(k%2?128:120))return -1;}}
    memset(seen,0,sizeof(seen));for(i=0;i<s->xm_instrument_count;i++){if(s->xm_instruments[i].index>=128||seen[s->xm_instruments[i].index]++)return -1;for(j=0;j<96;j++)if(s->xm_instruments[i].keymap[j]>128)return -1;}return 0;
}
void rpmc_free(rpmc_song *s){size_t i;if(!s)return;free(s->title.bytes);free(s->message.bytes);free(s->events.bytes);free(s->sample_data.bytes);free(s->source_data.bytes);free(s->order);free(s->order_rows);free(s->pattern_rows);free(s->channel_state);if(s->samples)for(i=0;i<s->sample_count;i++)free(s->samples[i].name.bytes);if(s->instruments)for(i=0;i<s->instrument_count;i++)free(s->instruments[i].name.bytes);if(s->xm_instruments)for(i=0;i<s->xm_instrument_count;i++)free(s->xm_instruments[i].name.bytes);free(s->samples);free(s->instruments);free(s->xm_instruments);memset(s,0,sizeof(*s));}
int rpmc_decode(const uint8_t *bytes,size_t n,rpmc_song *s){reader r={bytes,n,0,0};const uint8_t *p;size_t i,j,sample_bytes,event_bytes=0;if(!s)return -1;memset(s,0,sizeof(*s));if(!bytes||n>LIMIT||n<8||memcmp(bytes,"RPMC",4))return -1;r.p=4;if(get(&r,2)!=26||get(&r,2))return -1;
    s->format=get(&r,4);s->length=get(&r,4);s->channels=get(&r,4);s->patterns=get(&r,4);s->speed=get(&r,4);s->bpm=get(&r,4);s->global_volume=get(&r,4);s->mix_volume=get(&r,4);s->pan_separation=get(&r,4);s->flags=get(&r,4);s->restart=get(&r,4);s->message=string(&r);p=take(&r,1152);if(!p)goto fail;memcpy(s->macros,p,1152);s->sample_count=get(&r,4);sample_bytes=get(&r,4);s->title=string(&r);
    if(!s->length||s->length>2048||!s->channels||s->channels>64||!s->patterns||s->patterns>2048||s->sample_count>128||sample_bytes>LIMIT||r.error)goto fail;
    s->order=calloc(s->length,sizeof(*s->order));s->order_rows=calloc(s->length,sizeof(*s->order_rows));s->channel_state=calloc(s->channels,sizeof(*s->channel_state));s->pattern_rows=calloc(s->patterns,sizeof(*s->pattern_rows));s->samples=calloc(s->sample_count?s->sample_count:1,sizeof(*s->samples));if(!s->order||!s->order_rows||!s->channel_state||!s->pattern_rows||!s->samples)goto fail;
    for(i=0;i<s->length;i++)s->order[i]=get(&r,4);for(i=0;i<s->length;i++)s->order_rows[i]=get(&r,4);for(i=0;i<s->channels;i++){s->channel_state[i].pan=get(&r,4);s->channel_state[i].volume=get(&r,4);s->channel_state[i].muted=get(&r,4);s->channel_state[i].surround=get(&r,4);}for(i=0;i<s->patterns;i++){s->pattern_rows[i]=get(&r,4);if(!s->pattern_rows[i]||s->pattern_rows[i]>4096)goto fail;event_bytes+=(size_t)s->pattern_rows[i]*s->channels*44;}
    for(i=0;i<s->sample_count;i++){s->samples[i].index=(uint16_t)get(&r,2);p=take(&r,1399);if(!p)goto fail;memcpy(s->samples[i].meta,p,1399);s->samples[i].name=string(&r);}
    s->instrument_count=get(&r,2);if(s->instrument_count>256)goto fail;s->instruments=calloc(s->instrument_count?s->instrument_count:1,sizeof(*s->instruments));if(!s->instruments)goto fail;for(i=0;i<s->instrument_count;i++){s->instruments[i].index=(uint16_t)get(&r,2);p=take(&r,1399);if(!p)goto fail;memcpy(s->instruments[i].meta,p,1399);s->instruments[i].name=string(&r);p=take(&r,240);if(!p)goto fail;memcpy(s->instruments[i].keymap,p,240);}
    s->xm_instrument_count=get(&r,2);if(s->xm_instrument_count>128)goto fail;s->xm_instruments=calloc(s->xm_instrument_count?s->xm_instrument_count:1,sizeof(*s->xm_instruments));if(!s->xm_instruments)goto fail;for(i=0;i<s->xm_instrument_count;i++){s->xm_instruments[i].index=(uint16_t)get(&r,2);s->xm_instruments[i].name=string(&r);p=take(&r,96);if(!p)goto fail;for(j=0;j<96;j++)s->xm_instruments[i].keymap[j]=p[j];}
    s->events=data(&r,event_bytes);s->sample_data=data(&r,sample_bytes);i=get(&r,4);s->source_data=data(&r,i);if(r.error||r.p!=n||valid(s))goto fail;return 0;
fail:rpmc_free(s);return -1;
}
int rpmc_encode(const rpmc_song *s,uint8_t **bytes,size_t *n){writer w={NULL,0,0,0};size_t i;if(!bytes||!n)return -1;*bytes=NULL;*n=0;if(valid(s))return -1;write(&w,(const uint8_t *)"RPMC",4);put(&w,26,2);put(&w,0,2);put(&w,s->format,4);put(&w,s->length,4);put(&w,s->channels,4);put(&w,s->patterns,4);put(&w,s->speed,4);put(&w,s->bpm,4);put(&w,s->global_volume,4);put(&w,s->mix_volume,4);put(&w,s->pan_separation,4);put(&w,s->flags,4);put(&w,s->restart,4);put_string(&w,s->message);write(&w,s->macros,1152);put(&w,(uint32_t)s->sample_count,4);put(&w,(uint32_t)s->sample_data.size,4);put_string(&w,s->title);
    for(i=0;i<s->length;i++)put(&w,s->order[i],4);for(i=0;i<s->length;i++)put(&w,s->order_rows[i],4);for(i=0;i<s->channels;i++){put(&w,s->channel_state[i].pan,4);put(&w,s->channel_state[i].volume,4);put(&w,s->channel_state[i].muted,4);put(&w,s->channel_state[i].surround,4);}for(i=0;i<s->patterns;i++)put(&w,s->pattern_rows[i],4);
    for(i=0;i<s->sample_count;i++){put(&w,s->samples[i].index,2);write(&w,s->samples[i].meta,1399);put_string(&w,s->samples[i].name);}put(&w,(uint32_t)s->instrument_count,2);for(i=0;i<s->instrument_count;i++){put(&w,s->instruments[i].index,2);write(&w,s->instruments[i].meta,1399);put_string(&w,s->instruments[i].name);write(&w,s->instruments[i].keymap,240);}put(&w,(uint32_t)s->xm_instrument_count,2);for(i=0;i<s->xm_instrument_count;i++){put(&w,s->xm_instruments[i].index,2);put_string(&w,s->xm_instruments[i].name);write(&w,s->xm_instruments[i].keymap,96);}write(&w,s->events.bytes,s->events.size);write(&w,s->sample_data.bytes,s->sample_data.size);put(&w,(uint32_t)s->source_data.size,4);write(&w,s->source_data.bytes,s->source_data.size);if(w.error){free(w.s);return -1;}*bytes=w.s;*n=w.n;return 0;
}
