/* $Id: p_block.c,v 1.1 2004/12/18 17:29:32 rocky Exp $ Copyright (C) 2004 Rocky Bernstein Copyright (C) 1998 Monty xiphmont@mit.edu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "p_block.h" #include #include linked_list *new_list(void *(*newp)(void),void (*freep)(void *)) { linked_list *ret=calloc(1,sizeof(linked_list)); ret->new_poly=newp; ret->free_poly=freep; return(ret); } linked_element *add_elem(linked_list *l,void *elem) { linked_element *ret=calloc(1,sizeof(linked_element)); ret->stamp=l->current++; ret->ptr=elem; ret->list=l; if(l->head) l->head->prev=ret; else l->tail=ret; ret->next=l->head; ret->prev=NULL; l->head=ret; l->active++; return(ret); } linked_element * new_elem(linked_list *list) { void *new=list->new_poly(); return(add_elem(list,new)); } void free_elem(linked_element *e,int free_ptr) { linked_list *l=e->list; if(free_ptr)l->free_poly(e->ptr); if(e==l->head) l->head=e->next; if(e==l->tail) l->tail=e->prev; if(e->prev) e->prev->next=e->next; if(e->next) e->next->prev=e->prev; l->active--; free(e); } void free_list(linked_list *list,int free_ptr) { while(list->head) free_elem(list->head,free_ptr); free(list); } void *get_elem(linked_element *e) { return(e->ptr); } linked_list *copy_list(linked_list *list) { linked_list *new=new_list(list->new_poly,list->free_poly); linked_element *i=list->tail; while(i){ add_elem(new,i->ptr); i=i->prev; } return(new); } /**** C_block stuff ******************************************************/ static c_block * i_cblock_constructor(cdrom_paranoia_t *p) { c_block *ret=calloc(1,sizeof(c_block)); return(ret); } void i_cblock_destructor(c_block *c) { if(c){ if(c->vector)free(c->vector); if(c->flags)free(c->flags); c->e=NULL; free(c); } } c_block * new_c_block(cdrom_paranoia_t *p) { linked_element *e=new_elem(p->cache); c_block *c=e->ptr; c->e=e; c->p=p; return(c); } void free_c_block(c_block *c) { /* also rid ourselves of v_fragments that reference this block */ v_fragment *v=v_first(c->p); while(v){ v_fragment *next=v_next(v); if(v->one==c)free_v_fragment(v); v=next; } free_elem(c->e,1); } static v_fragment * i_vfragment_constructor(void) { v_fragment *ret=calloc(1,sizeof(v_fragment)); return(ret); } static inline void i_v_fragment_destructor(v_fragment *v) { free(v); } v_fragment * new_v_fragment(cdrom_paranoia_t *p, c_block *one, long int begin, long int end, int last) { linked_element *e=new_elem(p->fragments); v_fragment *b=e->ptr; b->e=e; b->p=p; b->one=one; b->begin=begin; b->vector=one->vector+begin-one->begin; b->size=end-begin; b->lastsector=last; return(b); } void free_v_fragment(v_fragment *v) { free_elem(v->e,1); } c_block * c_first(cdrom_paranoia_t *p) { if(p->cache->head) return(p->cache->head->ptr); return(NULL); } c_block * c_last(cdrom_paranoia_t *p) { if(p->cache->tail) return(p->cache->tail->ptr); return(NULL); } c_block * c_next(c_block *c) { if(c->e->next) return(c->e->next->ptr); return(NULL); } c_block * c_prev(c_block *c) { if(c->e->prev) return(c->e->prev->ptr); return(NULL); } v_fragment * v_first(cdrom_paranoia_t *p) { if(p->fragments->head){ return(p->fragments->head->ptr); } return(NULL); } v_fragment * v_last(cdrom_paranoia_t *p) { if(p->fragments->tail) return(p->fragments->tail->ptr); return(NULL); } v_fragment * v_next(v_fragment *v) { if(v->e->next) return(v->e->next->ptr); return(NULL); } v_fragment * v_prev(v_fragment *v) { if(v->e->prev) return(v->e->prev->ptr); return(NULL); } void recover_cache(cdrom_paranoia_t *p) { linked_list *l=p->cache; /* Are we at/over our allowed cache size? */ while(l->active>p->cache_limit) /* cull from the tail of the list */ free_c_block(c_last(p)); } int16_t * v_buffer(v_fragment *v) { if(!v->one)return(NULL); if(!cv(v->one))return(NULL); return(v->vector); } /* alloc a c_block not on a cache list */ c_block * c_alloc(int16_t *vector,long begin,long size) { c_block *c=calloc(1,sizeof(c_block)); c->vector=vector; c->begin=begin; c->size=size; return(c); } void c_set(c_block *v,long begin){ v->begin=begin; } /* pos here is vector position from zero */ void c_insert(c_block *v,long pos,int16_t *b,long size) { int vs=cs(v); if(pos<0 || pos>vs)return; if(v->vector) v->vector=realloc(v->vector,sizeof(int16_t)*(size+vs)); else v->vector=malloc(sizeof(int16_t)*size); if(posvector+pos+size,v->vector+pos, (vs-pos)*sizeof(int16_t)); memcpy(v->vector+pos,b,size*sizeof(int16_t)); v->size+=size; } void c_remove(c_block *v,long cutpos,long cutsize) { int vs=cs(v); if(cutpos<0 || cutpos>vs)return; if(cutpos+cutsize>vs)cutsize=vs-cutpos; if(cutsize<0)cutsize=vs-cutpos; if(cutsize<1)return; memmove(v->vector+cutpos,v->vector+cutpos+cutsize, (vs-cutpos-cutsize)*sizeof(int16_t)); v->size-=cutsize; } void c_overwrite(c_block *v,long pos,int16_t *b,long size){ int vs=cs(v); if(pos<0)return; if(pos+size>vs)size=vs-pos; memcpy(v->vector+pos,b,size*sizeof(int16_t)); } void c_append(c_block *v, int16_t *vector, long size) { int vs=cs(v); /* update the vector */ if(v->vector) v->vector=realloc(v->vector,sizeof(int16_t)*(size+vs)); else v->vector=malloc(sizeof(int16_t)*size); memcpy(v->vector+vs,vector,sizeof(int16_t)*size); v->size+=size; } void c_removef(c_block *v, long cut){ c_remove(v,0,cut); v->begin+=cut; } /**** Initialization *************************************************/ void i_paranoia_firstlast(cdrom_paranoia_t *p) { int i; cdrom_drive_t *d=p->d; p->current_lastsector=-1; for(i=cdda_sector_gettrack(d,p->cursor);icurrent_lastsector=cdda_track_lastsector(d,i-1); if(p->current_lastsector==-1) p->current_lastsector=cdda_disc_lastsector(d); p->current_firstsector=-1; for(i=cdda_sector_gettrack(d,p->cursor);i>0;i--) if(!cdda_track_audiop(d,i)) p->current_firstsector=cdda_track_firstsector(d,i+1); if(p->current_firstsector==-1) p->current_firstsector=cdda_disc_firstsector(d); } cdrom_paranoia_t * paranoia_init(cdrom_drive_t *d) { cdrom_paranoia_t *p=calloc(1,sizeof(cdrom_paranoia_t)); p->cache=new_list((void *)&i_cblock_constructor, (void *)&i_cblock_destructor); p->fragments=new_list((void *)&i_vfragment_constructor, (void *)&i_v_fragment_destructor); p->readahead=150; p->sortcache=sort_alloc(p->readahead*CD_FRAMEWORDS); p->d=d; p->dynoverlap=MAX_SECTOR_OVERLAP*CD_FRAMEWORDS; p->cache_limit=JIGGLE_MODULO; p->enable=PARANOIA_MODE_FULL; p->cursor=cdda_disc_firstsector(d); p->lastread=LONG_MAX; /* One last one... in case data and audio tracks are mixed... */ i_paranoia_firstlast(p); return(p); }