Files
icex2 19a31fe2e9 Add legacy iidx tooling that I had lying around.
Credits also go to Grim for hooking me up with the "old 2dx tooling"
2021-04-25 13:06:47 +02:00

1021 lines
22 KiB
C
Executable File

//1.h
//deals with parsing the bms files for events and converting them into a chain of .1 file events and other .1 related stuff
//largely based on DXAC1.pas (QQQ)
//2dx ac framerate
#define FPS_GOLD 60.05 //According to Tau GOLD uses this framerate instead of the standard 60.04 for internal computations
#define FPS_DD 59.95 //framerate for DistorteD and up
#define PRELOAD_DELAY 200 //specifies the maximum keysound preload delay before the actual key in ms
#define ENDSONG_DELAY 3000 //end a song 3 seconds after the last measure
//BMS event/channel list
#define BMS_BPM_CUST 8
#define BMS_BPM 3
#define BMS_STOP 9
#define BMS_AUTOPLAY 1
#define BMS_P1_1 11
#define BMS_P1_2 12
#define BMS_P1_3 13
#define BMS_P1_4 14
#define BMS_P1_5 15
#define BMS_P1_6 18
#define BMS_P1_7 19
#define BMS_P1_S 16
#define BMS_P2_1 21
#define BMS_P2_2 22
#define BMS_P2_3 23
#define BMS_P2_4 24
#define BMS_P2_5 25
#define BMS_P2_6 28
#define BMS_P2_7 29
#define BMS_P2_S 26
//.1 file event type list
#define ONE_AUTOPLAY 7
#define ONE_TYPE_P1 0
#define ONE_TYPE_P2 1
#define ONE_KEYCHANGE1 2
#define ONE_KEYCHANGE2 3
#define ONE_TYPE_BPM 4
#define ONE_MEASURESIZE 5
#define ONE_ENDSONG 6
#define ONE_JUDGEMENT 8
#define ONE_MEASURE 0xC
#define ONE_NOTECOUNT 0x10
#define ENDSEQ 0x7FFFFFFF
#define ONE_DUMMY 0xF0
#define ONE_STOP 0xF1 //this is not the actual event, just a marker for later
#define ONE_LONG_P1 0xF2 //same here
#define ONE_LONG_P2 0xF3 //same here
#define ONE_ENDLONG_P1 0xF4 //lnobj handling
#define ONE_ENDLONG_P2 0xF5
#define EVENTSIZE 8
//we build a linked list of events so we can easily append stuff
typedef struct
{
//entries are the same as in the .1 file
unsigned int time;
byte type;
byte data1;
unsigned short int data2;
//longnote duration
unsigned short int longdur;
void *prev;
void *next;
} event;
//this will hold pointers to the first node for each chart
event *onecharts[CHARTCOUNT];
//These are from D.C.Fish (Gold AC)
//note to self: look up other charts
unsigned short int judgements[6] = {0xF0, 0xFA, 0xFF, 0x3, 0x8, 0x12};
//offsets inside the .1 header for chart i
int offsets[CHARTCOUNT] = {0, 8, 16, 24, 48, 56, 64};
//the write order inside the official .1 charts is h7, h14, n7, n14, a7, a14, b, maybe this matters
//anyway, it's not much hassle, so let's write them in "official" order
int worder[7] = {0, 4, 1, 5, 2, 6, 3};
int onesize; //size of the whole .1 file
byte *onefile;
//inserts an event into the chain for chart i while keeping the timestamps in ascending order
//this might seem overly complicated, but for several reasons we need to know where exactly
//events with equal timestamps are positioned
void add_event(int i, event ev)
{
event *temp, *ev_n;
if(!onecharts[i])
{
onecharts[i] = malloc(sizeof(event));
memcpy(onecharts[i], &ev, sizeof(event));
onecharts[i]->prev = onecharts[i]->next = NULL;
return;
}
temp = onecharts[i];
while(temp->next != NULL && temp->time < ev.time)
{
temp = temp->next;
}
ev_n = malloc(sizeof(event)); //create the new event to insert
memcpy(ev_n, &ev, sizeof(event)); //and copy what we need
if(temp->time < ev.time) //this is an append or insert after the current note
{
if(temp->next)
((event*)temp->next)->prev = ev_n;
ev_n->next = temp->next;
temp->next = ev_n;
ev_n->prev = temp;
}
else //this is an insert before the current node
{
if(temp->prev)
((event*)temp->prev)->next = ev_n;
else //this is to become the first element
onecharts[i] = ev_n;
ev_n->next = temp;
ev_n->prev = temp->prev;
temp->prev = ev_n;
}
}
//deletes an event from the chain
void delete_event(event *ev)
{
event *pr, *nx;
pr = ev->prev;
nx = ev->next;
if(pr)
pr->next = nx;
if(nx)
nx->prev = pr;
free(ev);
}
//main parser logic
//converts the bms events to .1 events
//this does _not_ add the standard events yet
void convert_to_1_events()
{
int i,j,k;
char measure_c[4]; //use this to store the first 3 bytes after '#'. I hate writing parsers
int measure,channel;
float mtime;
int denom, nume; //denominator and nominator of a note inside a measure
byte ev_type, ev_type_keep;
float ev_time;
byte ev_data1;
int bms_ev;
event temp;
int ln;
int flip1, flip2;
unsigned short int ev_data2;
byte *data;
measure_c[3] = '\0';
flip1 = 0;
flip2 = 0;
temp.next = temp.prev = NULL;
printf("converting bms charts to .1 charts\n");
for(i = 0; i < CHARTCOUNT; i++)
{
if(!bms[i].length) continue;
printf("chart %s: ", CHARTNAMES[i]);
data = bms[i].data;
for(j = 1; j < bms[i].length; j++)
{
//find a candidate (parse for "#XXXYY:")
if(data[j] == '#' && data[j+6] == ':')
{
measure_c[0] = data[j+1];
measure_c[1] = data[j+2];
measure_c[2] = data[j+3];
sscanf(measure_c, "%d", &measure);
sscanf(data+j+4, "%d", &channel);
ev_data1 = 0;
ev_data2 = 0;
ev_time = 0;
ev_type = 0;
if(channel >= 50) //longnotes
{
channel -= 40;
ln = 1;
//printf("!");
}
else
{
ln = 0;
}
//check the channel and if we support it, if so set the event type and data type
//don't be scared by the fancy switch magic, we just evaluate bms events to .1 events in a tricky way to save typing work :P
switch(channel)
{
case BMS_P1_S:
ev_data1 += 4;
case BMS_P1_6:
case BMS_P1_7:
ev_data1 -= 2;
case BMS_P1_1:
case BMS_P1_2:
case BMS_P1_3:
case BMS_P1_4:
case BMS_P1_5:
ev_data1 += channel - 11;
ev_type = ONE_TYPE_P1;
break;
case BMS_P2_S:
ev_data1 += 4;
case BMS_P2_6:
case BMS_P2_7:
ev_data1 -= 2;
case BMS_P2_1:
case BMS_P2_2:
case BMS_P2_3:
case BMS_P2_4:
case BMS_P2_5:
ev_data1 += channel - 21;
ev_type = ONE_TYPE_P2;
break;
case BMS_BPM:
case BMS_BPM_CUST:
ev_type = ONE_TYPE_BPM;
break;
case BMS_AUTOPLAY:
ev_type = ONE_AUTOPLAY;
break;
case BMS_STOP:
ev_type = ONE_STOP;
break;
default:
//printf("%d ", channel);
continue; //unsupported event type, we skip this
}
//calculate the start of the measure
mtime = 0;
for(k = 0; k < measure; k++)
mtime += bms[i].msize[k];
//now that we've got the measure and the channel, count the number of events for this measure to get the denominator
denom = 0;
for(k = j+7/*after the ':'*/; (k<=bms[i].length) && (data[k] != '\n') && (data[k] != '\r') && (data[k] != '#'); k+=2) //just to be sure...
denom++;
k = j + 7;
nume = 0;
ev_type_keep = ev_type;
//now for the interesting part
for(k = j+7; nume < denom; k+=2)
{
ev_time = mtime + ((float)nume/(float)denom)*bms[i].msize[measure];
ev_time *= 4. * 60. * 1000./ bms[i].bpm; //timestamp in milliseconds
nume++;
ev_type = ev_type_keep; //in case we run into a lnobj on the way
bms_ev = strtoi(data+k);
if(!bms_ev)
{
continue; //'00', skip this
}
if(channel == BMS_AUTOPLAY)
{
ev_data2 = bms[i].ref[bms_ev-1]; //remember that we always count from 0 while bms indices start from 1
}
else if(channel == BMS_BPM_CUST)
{
//printf("BPM change!\n");
if(((bms[i].bpmchange[bms_ev-1] - (int)bms[i].bpmchange[bms_ev-1]) < 0.01) || (bms[i].bpmchange[bms_ev-1] > 655.35))
{
//an interesting way to work around the short int limit
ev_data1 = 1;
ev_data2 = (unsigned short int)(bms[i].bpmchange[bms_ev-1]);
}
else
{
ev_data1 = 100;
ev_data2 = (unsigned short int)bms[i].bpmchange[bms_ev-1]*100;
}
}
else if(channel == BMS_BPM)
{
//printf("BPM change!\n");
ev_data1 = 1;
ev_data2 = (unsigned short)strtoi_h(data+k); //in this case we have to use hex, meh
}
else if(channel == BMS_STOP)
{
//printf("STOP!\n");
ev_data2 = (unsigned short)strtoi(data+k) - 1; //BMS starts indexing at 1 blablabla
//continue;
}
else if(channel >= 11 && channel <= 29) //keypress
{
//for now we store the keysound number in the key event; we create the actual keysound event later
if(bms_ev != bms[i].lnobj)
{
ev_data2 = bms[i].ref[bms_ev-1]; //data2 is the keysound index inside the .2dx file
}
else
{
ev_data2 = 0;
}
if(channel <= 19) //1player side
{
bms[i].numkeys[0]++;
if(ln) //longnote handling
{
ev_type = ONE_LONG_P1;
//don't count every second longnote event; notecount stays consistant
if(flip1)
{
bms[i].numkeys[0]--;
flip1 = 0;
}
else
{
flip1 = 1;
}
}
}
else //2player side
{
bms[i].numkeys[1]++;
if(ln)
{
ev_type = ONE_LONG_P2;
if(flip2)
{
bms[i].numkeys[1]--;
flip2 = 0;
}
else
{
flip2 = 1;
}
}
}
if(bms_ev == bms[i].lnobj)
{
//longnote end marker
if(channel <= 19)
{
ev_type = ONE_ENDLONG_P1;
bms[i].numkeys[0]--;
}
else
{
ev_type = ONE_ENDLONG_P2;
bms[i].numkeys[1]--;
}
//continue;
}
}
//now add the actual event to the list;
temp.time = ev_time;
temp.type = ev_type;
temp.data1 = ev_data1;
temp.data2 = ev_data2;
temp.longdur = 0;
//printf("time:%d channel:%d type:%d(bms:%d), data1:%d, data2:%d\n", (int)ev_time, channel, ev_type, bms_ev, ev_data1, bms[i].ref[bms_ev-1]);
add_event(i, temp);
if(i >= 4 && temp.type == ONE_TYPE_BPM)
{
add_event(i, temp); //for some reason these are added twice for double charts
}
}
//shortcut, skip parsed line
j = k;
}
}
printf("keypresses: %d(P1) %d(P2)\n", bms[i].numkeys[0], bms[i].numkeys[1]);
//printf("adding measure lines...\n");
//now that we're done with the normal events, add the measure lines
ev_time = 0;
temp.time = 0;
temp.type = ONE_MEASURE;
temp.data1 = 0;
temp.data2 = 0;
for(j = 0; j <= bms[i].mcount; j++)
{
temp.time = ev_time * 4. * 60. * 1000. / bms[i].bpm;
if(i >= 4) //for double charts, add a measure line for the 2p side, too
{
temp.data1 = 1;
add_event(i, temp);
}
temp.data1 = 0;
add_event(i, temp);
ev_time += bms[i].msize[j];
}
//we add this dummy event as the marker for the end of the last measure
//note that this is not really optimal for calculating the end of the song (will fix later)
temp.type = ONE_DUMMY;
temp.time = ev_time * 4. * 60. * 1000. / bms[i].bpm;
add_event(i, temp);
}
}
void add_standard_events()
{
int i,j,k;
event total_notes_p1, total_notes_p2;
event bpm; //starting bpm
event msize; //measure size
event timings; //timing windows
event endmark; //marks the end of the song
event endseq; //end sequence
event *temp;
int twop = 0; //double chart indicator
printf("adding standard events...");
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i])
continue;
if(i >= 4) twop = 1;
else twop = 0;
printf("%s...", CHARTNAMES[i]);
for(k = twop; k >= 0; k--) //double charts have two entries for everything, so add them in, too
{
memset(&total_notes_p1, 0, sizeof(event));
memset(&total_notes_p2, 0, sizeof(event));
memset(&bpm, 0, sizeof(event));
memset(&msize, 0, sizeof(event));
memset(&timings, 0, sizeof(event));
memset(&endmark, 0, sizeof(event));
memset(&endseq, 0, sizeof(event));
total_notes_p1.type = ONE_NOTECOUNT;
total_notes_p1.data2 = bms[i].numkeys[0];
total_notes_p2.type = ONE_NOTECOUNT;
total_notes_p2.data1 = 1;
total_notes_p2.data2 = bms[i].numkeys[1];
bpm.type = ONE_TYPE_BPM;
if((bms[i].bpm-(int)bms[i].bpm < 0.01) || (bms[i].bpm > 655.35)) //ass-check for fractional bpm, probably doesn't even matter
{
bpm.data1 = 1;
bpm.data2 = (unsigned short int)(bms[i].bpm);
}
else
{
bpm.data1 = 100;
bpm.data2 = (unsigned short int)bms[i].bpm*100;
}
msize.type = ONE_MEASURESIZE;
msize.data1 = 4;
msize.data2 = 4;
//add judgement events
timings.type = ONE_JUDGEMENT;
for(j = 5; j >= 0; j--)
{
timings.data1 = (byte)j;
timings.data2 = judgements[j];
add_event(i, timings);
}
add_event(i, msize);
add_event(i, bpm);
if(k != 1)
{
add_event(i, total_notes_p2);
add_event(i, total_notes_p1);
}
temp = onecharts[i];
while(temp->next) temp = temp->next; //temp->type == ONE_DUMMY
endmark.type = ONE_ENDSONG;
endmark.time = temp->time + ENDSONG_DELAY*(1-twop*(1-k)); //evil hack :D; ensures we add both the p1 and the p2 event at the same time
endmark.data1 = k;
add_event(i, endmark);
}
endseq.time = ENDSEQ;
add_event(i, endseq);
//kill the dummy event
temp = onecharts[i];
while(temp = temp->next)
{
if(temp->type == ONE_DUMMY)
{
delete_event(temp);
break; //only one dummy event per chart
}
}
}
PNEWLINE;
}
void handle_longnotes()
{
int i, player, time, count;
event *temp;
event *temp2;
int debug[8];
int ev_debug[8];
memset(debug, 0, 8*sizeof(int));
printf("processing longnotes (if neccesary)..");
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i])
continue;
printf("%s..", CHARTNAMES[i]);
temp = onecharts[i];
count = 0;
//find longnote events
do
{
//longnotes from the longnote channel
if(temp->type == ONE_LONG_P1 || temp->type == ONE_LONG_P2) //longnote
{
count++;
if(temp->type == ONE_LONG_P1)
{
player = ONE_TYPE_P1;
}
else
{
player = ONE_TYPE_P2;
}
//find the exact time the longnote ends
temp2 = temp;
while((temp2 = temp2->next))
{
if((temp2->type == temp->type) && (temp2->data1 == temp->data1)) //end the longnote
{
//chargenote handling for sirius
temp->longdur = temp2->time - temp->time;
delete_event(temp2); //kill the endnote
//bms[i].numkeys[player]--;
break;
}
}
//set correct event type
temp->type = player;
}
//longnotes from lnobj
if((temp->type == ONE_ENDLONG_P1) || (temp->type == ONE_ENDLONG_P2))
{
count++;
if(temp->type == ONE_ENDLONG_P1)
{
player = ONE_TYPE_P1;
}
else
{
player = ONE_TYPE_P2;
}
//find the previous keypress event
temp2 = temp;
while((temp2 = temp2->prev))
{
if((temp2->type == player) && (temp2->data1 == temp->data1) && (temp2->time < temp->time)) //same player, same lane, back in time
{
//set longnote duration
temp2->longdur = temp->time - temp2->time;
//printf("TI:%d TY:%d D1:%d D2:%d DUR:%d\n", temp2->time, temp2->type, temp2->data1, temp2->data2, temp2->longdur);
//kill lnobj event
temp = temp->prev;
temp2 = temp->next;
//printf(" TI:%d TY:%d D1:%d D2:%d DUR:%d\n", temp2->time, temp2->type, temp2->data1, temp2->data2, temp2->longdur);
delete_event(temp2);
break;
}
}
}
} while((temp = temp->next));
printf("(%d)...", count);
}
PNEWLINE;
}
//remove multiple keysound loads
void cleanup_1()
{
int i,j,k;
event *temp;
event *temp2;
event ref;
printf("cleaning up charts..");
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i])
continue;
printf("%s..", CHARTNAMES[i]);
memset(&ref, 0, sizeof(event));
//delete useless keysound loads; walk the chain once for the p1 side, once for the p2 side
for(k = 0; k <= 1; k++)
{
for(j = 0; j <= 7; j++)
{
ref.data2 = 0xFFFF; //just to be sure we don't accidently kill the first sample load
temp = onecharts[i];
do
{
if(temp->type == k+2 && temp->data1 == j) //soundchange for player k and key j
{
if(temp->data2 == ref.data2)
{
//printf("deleted a keysound event in %s: player:%d time:%d key:%d sound:%d\n", \
CHARTNAMES[i], k+1, temp->time, temp->data1, temp->data2);
temp2 = temp->next;
delete_event(temp);
temp = temp2;
if(!temp) break;
}
else
{
ref.data2 = temp->data2;
}
}
} while(temp = temp->next);
}
}
}
PNEWLINE;
}
void create_1()
{
int i;
int pos; //position
int size[CHARTCOUNT]; //size of the chains inside the .1 file
event *temp;
pos = 0x60; //start writing after the .1 header which is 96 bytes
memset(size, 0, sizeof(int)*CHARTCOUNT);
onesize = 96; //header is always the same size
printf("creating .1 file\n");
//first calculate the size for each event chain and the resulting size of the .1 file
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i])
continue;
temp = onecharts[i];
do
{
//printf("(%d %d) ", temp->time, temp->type);
size[i] += EVENTSIZE;
}
while(temp = temp->next);
onesize += size[i];
//printf("size for %s is %d\n", CHARTNAMES[i], size[i]);
}
onefile = malloc(onesize);
memset(onefile, 0, onesize);
//printf("onesize is %d\n", onesize);
//write the header and the chains to the buffer
for(i = 0; i < CHARTCOUNT; i++)
{
//printf("size[worder[i]]: %d\n", size[worder[i]]);
if(!onecharts[worder[i]])
continue;
memcpy(onefile+offsets[worder[i]], &pos, 4); //write the position of the chart
memcpy(onefile+offsets[worder[i]]+4, &size[worder[i]], 4); //write the size of the chart
//write out the events
temp = onecharts[worder[i]];
do
{
memcpy(onefile+pos, &temp->time, 4);
memcpy(onefile+pos+4, &temp->type, 1);
memcpy(onefile+pos+5, &temp->data1, 1);
memcpy(onefile+pos+6, &temp->data2, 2);
pos += 8;
}
while(temp = temp->next);
}
PNEWLINE;
}
void adjust_bpm()
{
int i;
float oldbpm = 0;
float newbpm = 0;
int stime = 0;
int tdiff = 0;
event *temp;
event *tadjust;
printf("adjusting for (possible) BPM changes...");
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i]) continue;
temp = onecharts[i];
oldbpm = bms[i].bpm;
//search for bpm change events and adjust all timestamps after those events accordingly
do
{
if(temp->type == ONE_TYPE_BPM)
{
newbpm = temp->data2 / temp->data1;
stime = temp->time;
if(oldbpm == newbpm) //no need to do anything
continue;
tadjust = temp;
//events are sorted by time so this works just peachy
while(tadjust = tadjust->next)
{
tdiff = tadjust->time - stime;
tdiff = (int)((float)tdiff * oldbpm / newbpm); //adjust the timestamp accordingly
tadjust->time = stime + tdiff;
}
oldbpm = newbpm;
}
}
while(temp = temp->next);
}
PNEWLINE;
}
void writeout_1()
{
FILE *f;
printf("writing output.1 to disk\n");
f = fopen("output.1", "wb");
fwrite(onefile, onesize, 1, f);
fclose(f);
}
void add_keysounds()
{
event *key;
event *p;
event temp;
int i;
int tdiff;
printf("adding keysound events...");
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i]) continue;
printf("%s...", CHARTNAMES[i]);
//search the chain for key events
key = onecharts[i];
do
{
if(key->type == ONE_TYPE_P1 || key->type == ONE_TYPE_P2)
{
//found a key event, let's add the keysound change for it
tdiff = 0;
//walk the timeline backwards and try to find the previous key event for the same key
p = key;
while(p = p->prev)
{
if(p->type == key->type && p->data1 == key->data1)
{
tdiff = p->time;
break;
}
}
//calculate the time difference between the two key events and place the keysound event right between them
tdiff = key->time - tdiff;
if(tdiff == key->time && key->time < PRELOAD_DELAY) //we found no key event before this one and the current key event is really close to time 0
; //do nothing as tdiff == key->time
else if(tdiff >= PRELOAD_DELAY*2) //the keys are far enough apart to use the standard preload delay
tdiff = PRELOAD_DELAY;
else //the keys are quite close together so place the keysound load event right between them
{
tdiff /= 2;
//printf("TDIFF: %d\n", tdiff);
}
memcpy(&temp, key, sizeof(event));
temp.time -= tdiff;
temp.type += 2; //preload event = playerside + 2
//printf("key: time:%d player:%d key:%d index:%d TDIFF:%d\n", key->time, key->type-2, key->data1+1, key->data2, tdiff);
//printf("keysoundevent: time:%d player:%d key:%d index:%d TDIFF:%d\n", temp.time, temp.type-2, temp.data1+1, temp.data2, tdiff);
add_event(i, temp);
if(key->longdur)
{
//keypress is a longnote
key->data2 = key->longdur;
}
else
key->data2 = 0;
}
}
while(key = key->next);
}
PNEWLINE;
}
void add_stop_events()
{
int i;
float bpm = 0;
float tdiff = 0;
int d1, d2;
event *temp;
event *helper;
event bpm_help;
printf("adjusting for (possible) stops...");
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i]) continue;
if(!bms[i].stopcount) continue;
printf("%s...", CHARTNAMES[i]);
temp = onecharts[i];
//search for stop events and adjust all timestamps after those events accordingly
do
{
if(temp->type == ONE_STOP)
{
//find the bpm for the current stop event
helper = onecharts[i];
do
{
if(helper->type == ONE_TYPE_BPM)
{
d1 = helper->data1;
d2 = helper->data2;
bpm = (float)helper->data2 / (float)helper->data1;
}
} while((helper = helper->next) && (helper->time <= temp->time));
//calculate the stop time
tdiff = bms[i].stops[temp->data2] / 192.;
tdiff = tdiff * 4. * 60. * 1000. / bpm;
//adjust all event times after the stop event
helper = temp->next;
do
{
if(helper->time > temp->time && helper->time != ENDSEQ) //in case there's another event at the same time as the stop
helper->time += tdiff;
}
while(helper = helper->next);
//add two bpm changes to simulate the stop
bpm_help.time = temp->time;
bpm_help.type = ONE_TYPE_BPM;
bpm_help.data1 = 100;
bpm_help.data2 = 100; //1 bpm (stop); iidx doesn't seem to support any lower
add_event(i, bpm_help);
if(i >= 4) //as always, add another event for double charts
add_event(i, bpm_help);
bpm_help.time += tdiff;
bpm_help.data1 = d1;
bpm_help.data2 = d2; //restore old bpm
add_event(i, bpm_help);
if(i >= 4)
add_event(i, bpm_help);
}
}
while(temp = temp->next);
//delete all stop events
temp = onecharts[i];
do
{
helper = temp->next;
if(helper && helper->type == ONE_STOP)
delete_event(helper);
}
while(temp = temp->next);
}
PNEWLINE;
}
void stretch_times(double factor)
{
int i;
event *temp;
for(i = 0; i < CHARTCOUNT; i++)
{
if(!onecharts[i]) continue;
temp = onecharts[i];
//walk the chain and multiply timestamps with factor
do
if(temp->time != ENDSEQ)
temp->time = (int)((float)temp->time * factor);
while(temp = temp->next);
}
}