Hey,
I've got the GFS_NwFread function and structure from the Workshop 95 slides, and it compiles and "runs".
Of course what I am trying to do is use it to read XL2's ZTP files, which have segmented data meant for a modelData_t struct.
It seems XL2 loads these in segments with GFS_Load, loading one segment has information relevant to loading the next segment.
Of course I'm here because I don't understand how exactly that works, I'm guessing the reason this doesn't work with GFS_NwFread is because I'm not arranging the memory addresses right.
I say that because it doesn't load any of the other models using the current Address variable called after my dysfunctional '"ztFileStream" function is run.
I imagine all I should need to do is read from the buffer into the address for a modelData_t struct...
..
but i don't know how to do that...
/e: Oh, you should probably know that the function is in ZT_LOAD_MODEL.c in ZT folder.
//Experimental
void * ztFileStream(Sint8 * filename, void * startAddress, entity_t * model)
{
memset_l((void*)startAddress,0x0000, (0xFFFE - ((Uint32)startAddress-(Uint32)LWRAM))); //Not 100% necessary, since data can just be overwritten, but good for testing and see how much data a level takes
void * currentAddress;
currentAddress = startAddress;
Sint32 fid = GFS_NameToId((Sint8*)filename);
GfsHn gfs;
Sint32 stat;
Sint32 nsct;
Sint32 bsize;
//Model Data is the destination of data.
modelData_t bufModel;
//Destination address?
void * ptr = &bufModel;
//Loading follows
gfs = GFS_Open(fid);
//Get sectors
GFS_GetFileSize(gfs, nsct, NULL, NULL);
bsize = 2048 * nsct;
// nsct = GFS_ByteToSct(gfs, bytesOf);
//modelData_t has an assumed size?
GFS_NwFread(gfs, nsct, (Uint32*)currentAddress, bsize);
//For loop is blank because this will run until GFS_Close, AKA always run until GFS_Close.
for(;;)
{
stat = GFS_NwExecOne(gfs);
if(stat == GFS_SVR_COMPLETED)
{
break;
}
//Copy gfs to modelData_t bufmodel???
memcpy_l((Sint32*)ptr, (Sint32*)(startAddress), (sizeof(modelData_t)));
/**ADDED**/
model->pos[X]=bufModel.Origin[X]; model->pos[Y]=bufModel.Origin[Y]; model->pos[Z]=bufModel.Origin[Z];
model->length[X]=bufModel.Length[X]; model->length[Y]=bufModel.Length[Y]; model->length[Z]=bufModel.Length[Z];
model->nbMeshes=bufModel.TOTAL_MESH;
//Get textures from GFS
Uint16 first_texture = loadTextures(startAddress, &bufModel);
//Get PDATA from GFS
// Sint32 bytesOff = (bufModel.TEXT_SIZE+(sizeof(modelData_t)))/2048;
// bytesOff = bufModel.TEXT_SIZE+(sizeof(modelData_t)) - (bytesOff*2048);
// currentAddress = (void*)(currentAddress + bytesOff);
currentAddress = loadPDATA((void*)currentAddress, model, &bufModel);
//Set textures from GFS
setTextures(first_texture, model, bufModel.TOTAL_MESH, true);
return currentAddress;
}
GFS_Close(gfs);
}
Upon further reading, I can't think of why this would not work.
What happens? It does not load. All loading stops.
//Experimental
void * ztFileStream(Sint8 * filename, void * startAddress, entity_t * model, Sint8 dummy)
{
memset_l((void*)startAddress,0x0000, (0xFFFE - ((Uint32)startAddress-(Uint32)LWRAM))); //Not 100% necessary, since data can just be overwritten, but good for testing and see how much data a level takes
void * currentAddress;
currentAddress = startAddress;
Sint32 fid = GFS_NameToId((Sint8*)filename);
GfsHn gfs;
Sint32 stat;
Sint32 nsct;
Sint32 bsize;
//Model Data is the destination of data.
modelData_t bufModel;
//Destination address?
void * ptr = &bufModel;
//Loading follows
gfs = GFS_Open(fid);
//Get sectors
GFS_GetFileSize(gfs, &nsct, NULL, NULL);
bsize = 2048 * nsct;
// nsct = GFS_ByteToSct(gfs, bytesOf);
//modelData_t has an assumed size?
GFS_NwFread(gfs, nsct, (Uint32*)currentAddress, bsize);
//For loop is blank because this will run until GFS_Close, AKA always run until GFS_Close.
for(;;)
{
stat = GFS_NwExecOne(gfs);
if(stat == GFS_SVR_COMPLETED)
{
break;
}
//Copy gfs to modelData_t bufmodel???
memcpy_l((Sint32*)ptr, (Sint32*)(startAddress), (sizeof(modelData_t)));
/**ADDED**/
model->pos[X]=bufModel.Origin[X]; model->pos[Y]=bufModel.Origin[Y]; model->pos[Z]=bufModel.Origin[Z];
model->length[X]=bufModel.Length[X]; model->length[Y]=bufModel.Length[Y]; model->length[Z]=bufModel.Length[Z];
model->nbMeshes=bufModel.TOTAL_MESH;
/**Load the texture list (using an offset to allow DMA transfer)**/
Uint16 first_texture = loadTextures(startAddress, &bufModel);
/**Load PDATA**/
Sint32 bytesOff = bufModel.TEXT_SIZE+(sizeof(modelData_t)) - (bytesOff*2048);
currentAddress = (void*)(currentAddress + bytesOff);
currentAddress = loadPDATA((void*)currentAddress, model, &bufModel);
/**Set textures**/
setTextures(first_texture, model, bufModel.TOTAL_MESH, true);
}
GFS_Close(gfs);
return currentAddress;
}
So what do we do:
1 - we set a new address so we don't overwrite the last data
2 - that address is now the current address
3 - we get a file ID from the file name
4 - variable and struct setup
? I question why bufModel is modelData_t and not entity_t, tried it one way or another it doesn't function properly
5 - Set GFS and GFS handler
6 - Get the file size in sectors, we don't need anything else (they are NULL).
6 - Set the size in bytes based on the sector size
7 - Queue the command NwFread for the file with current Address as the buffer area
8 - Execute NwFread in the open gfs
9 - When the server is complete with the file, stop the execution and
10 - memcpy destination bufModel, the source startAddress (which has never been set separate from currentAddress at this point BUT setting it to current causes unknown code crash), the size copied is the size of... you know.
? Why would current crash when it is no different from start...
? It doesn't crash or halt if everything past memcpy before GFS_Close is commented out.
11 What follows,
taking the contents of the buffered model data and putting it into the model which is the desired entity_t struct.
Truly, I know I don't really understand what is happening. Especially not enough to make a callback that could put the binary data into an entity_t struct from the Jo engine async read function.
I'm thinking in a forum post.
I got it working now. For some reason though I need to put it in a function that is in a jo callback area (as in, run it when jo_core is running). /e: Do note that is the INTENDED USE, ANYWAY.
I still need to work in some refinements, like getting it to load sector-by-sector and file-by-file so it doesn't pause everything else.
I might even see about doing memcpy in smaller increments.
Some manner of smooth file streaming like that must be possible, not for any specific purpose, just a technical curiosity. Thanks for your help XL2. You were right about the offset.
//Experimental
void * ztFileStream(Sint8 * filename, void * startAddress, entity_t * model, Sint8 dummy)
{
memset_l((void*)startAddress,0x0000, (0xFFFE - ((Uint32)startAddress-(Uint32)LWRAM))); //Not 100% necessary, since data can just be overwritten, but good for testing and see how much data a level takes
void * currentAddress;
currentAddress = startAddress;
//Data of read structure
Sint32 fid = GFS_NameToId((Sint8*)filename);
GfsHn gfs;
Sint32 nsct;
Sint32 fsize;
//Sector step of reading.
Uint8 rd_step = 2;
//Data of read-in-process
Sint32 stat;
Sint32 rdsize;
Sint32 i;
//Model Data is the destination of data in the work area.
modelData_t bufModel;
//Destination address?
void * ptr = &bufModel;
//Loading follows
gfs = GFS_Open(fid);
//Get sectors
GFS_GetFileSize(gfs, &nsct, NULL, NULL);
GFS_GetFileInfo(gfs, NULL, NULL, &fsize, NULL);
//Buffers are the destination of data in the CD area.
Uint32 buf1[rd_step * nsct / 4];
Uint8 *proc_bp;
// jo_printf(0, 18, "(%i)", nsct);
// jo_printf(0, 20, "(%i)", fsize);
// jo_printf(0, 22, "(%i)", proc_bp);
// jo_printf(0, 24, "(%i)", rdsize);
//Read from CD to CD buffer
GFS_NwCdRead(gfs, fsize);
GFS_SetTransPara(gfs, nsct);
GFS_NwFread(gfs, nsct, (Uint32*)currentAddress, fsize);
do{
GFS_NwExecOne(gfs);
GFS_NwGetStat(gfs, &stat, &rdsize);
}while (stat != GFS_SVR_COMPLETED && rdsize < fsize);
GFS_Close(gfs);
/**Copy gfs to modelData_t bufmodel???**/
memcpy_l((Sint32*)ptr, (Sint32*)(currentAddress), sizeof(modelData_t));
/**ADDED**/
model->pos[X]=bufModel.Origin[X]; model->pos[Y]=bufModel.Origin[Y]; model->pos[Z]=bufModel.Origin[Z];
model->length[X]=bufModel.Length[X]; model->length[Y]=bufModel.Length[Y]; model->length[Z]=bufModel.Length[Z];
model->nbMeshes=bufModel.TOTAL_MESH;
Uint16 first_texture = loadTextures(currentAddress, &bufModel);
Sint32 bytesOff = bufModel.TEXT_SIZE+(sizeof(modelData_t));
currentAddress = (void*)(currentAddress + bytesOff);
currentAddress = loadPDATA((void*)(currentAddress), model, &bufModel);
currentAddress = (void*)(currentAddress + (bufModel.PDATA_SIZE + sizeof(modelData_t)));
setTextures(first_texture, model, bufModel.TOTAL_MESH, true);
// jo_printf(0, 6, "(%i)", bufModel->TOTAL_MESH);
// jo_printf(0, 8, "(%i)", bufModel->PDATA_SIZE);
// jo_printf(0, 12, "(%i)", model->nbMeshes);
return currentAddress;
}
Of course, make sure this only runs once.
Funny I should go on double (now triple) posting like that. I found an arrangement with a GOTO statement that can pass over but safely complete the sluggish parts of the loop IF the function is continuously called. This is an important step in the right direction.
Uint8 rd_frames = 0;
Uint8 curRdFrame = 0;
//Experimental
void * ztFileStream(Sint8 * filename, void * startAddress, entity_t * model, Sint8 evenStep, Sint8 oddStep)
{
memset_l((void*)startAddress,0x0000, (0xFFFE - ((Uint32)startAddress-(Uint32)LWRAM))); //Not 100% necessary, since data can just be overwritten, but good for testing and see how much data a level takes
void * currentAddress;
currentAddress = startAddress;
//Data of read structure
Sint32 fid = GFS_NameToId((Sint8*)filename);
GfsHn gfs;
Sint32 nsct;
Sint32 fsize;
//Accepted rate of reading per frame (33 ms)
//Note to self: If your data is already byte size information, do not annotate with sizeOf. Thanks.
Uint32 rt_step = 4096;
Uint8 rt_sector = rt_step / 2048;
//Data of read-in-process
Sint32 stat;
Uint32 rdsize;
//Model Data is the destination of data in the work area.
modelData_t bufModel;
//Destination address?
void * ptr = &bufModel;
//Loading follows
gfs = GFS_Open(fid);
//Get sectors
//HEY! SBL DOCUMENTATION IS WRONG! THIRD ITEM NSCT IS GFS SECTOR COUNT. SECOND ITEM IS CD SECTOR SIZE.
GFS_GetFileSize(gfs, NULL, &nsct, NULL);
GFS_GetFileInfo(gfs, NULL, NULL, &fsize, NULL);
//How many frames are we reading?
if(rt_step < fsize){
rd_frames = (fsize + (rt_step - 1))/(rt_step);
}
jo_printf(0, 8, "(%i)", nsct);
jo_printf(10, 8, "(sectors)");
jo_printf(0, 9, "(%i)", fsize);
jo_printf(10, 9, "(file size)");
//Read from CD to CD buffer
//CONCEPT: NwFread specifies only a fetch from CD buffer. If no such read has been specified, it will carry it out itself.
//However, if you specify a read from CD, repeats of the fetch command will only fetch from the CD buffer and will not give errors for data remainders.
//Now I understand how the program on SBL Document 1 Page 18 works. It is incomplete; they assume the program is a system callback, but it works.
GFS_SetTmode(gfs, GFS_TMODE_CPU);
GFS_SetGmode(gfs, GFS_GMODE_ERASE);
GFS_SetReadPara(gfs, rt_step);
GFS_SetTransPara(gfs, rt_sector);
if(curRdFrame >= rd_frames){
goto RUNARC;
}
GFS_NwCdRead(gfs, fsize);
for( ; curRdFrame <= rd_frames ; ){
GFS_NwFread(gfs, rt_sector, (Uint32*)(currentAddress +(curRdFrame * rt_step)), rt_step);
do{
GFS_NwExecServer(gfs);
GFS_NwGetStat(gfs, &stat, &rdsize);
jo_printf(0, 15, "(%i)", curRdFrame);
jo_printf(4, 15, "(%i)", rd_frames);
}while (stat != GFS_SVR_COMPLETED && rdsize < rt_step);
curRdFrame++;
}
jo_printf(0, 11, "(%i)", rdsize);
jo_printf(10, 11, "(fetched filesize)");
GFS_Close(gfs);
// Copy gfs to modelData_t bufmodel???
memcpy_l((Sint32*)ptr, (Sint32*)(currentAddress), sizeof(modelData_t));
//ADDED
model->pos[X]=bufModel.Origin[X]; model->pos[Y]=bufModel.Origin[Y]; model->pos[Z]=bufModel.Origin[Z];
model->length[X]=bufModel.Length[X]; model->length[Y]=bufModel.Length[Y]; model->length[Z]=bufModel.Length[Z];
model->nbMeshes=bufModel.TOTAL_MESH;
Uint16 first_texture = loadTextures(currentAddress, &bufModel);
Sint32 bytesOff = bufModel.TEXT_SIZE+(sizeof(modelData_t));
currentAddress = (void*)(currentAddress + bytesOff);
currentAddress = loadPDATA((void*)(currentAddress), model, &bufModel);
currentAddress = (void*)(currentAddress + (bufModel.PDATA_SIZE + sizeof(modelData_t)));
setTextures(first_texture, model, bufModel.TOTAL_MESH, true);
RUNARC:
jo_printf(0, 13, "(%i)", bufModel.TOTAL_MESH);
jo_printf(3, 13, "(%i)", bufModel.PDATA_SIZE);
jo_printf(10, 13, "(%i)", model->nbMeshes);
return currentAddress;
}
An update.
Unlike previous incantations here, this one somewhat accomplishes the goals I have set out for.
I mean it does read from the CD in a non-linear fashion and puts it in the work area.
There's two problems though.
- It is painfully slow. If you think it's bad in an emulator, try it on real hardware. This structure of code is just untenable. I need a different approach.
- The data is corrupted somehow. I would like to solve that before I moved on to something else, but there's clearly no reason to so I am going to move on to a different command structure.
I have also identified a few things, such as GFS commands being a significant contributor to slowdown, although the primary slow-down on real hardware is going to be any loading loop as the CD system is not lightning fast (obviously, its a CD).
volatile Uint32 rd_frames = 0;
volatile Uint32 curRdFrame = 0;
bool file_reading;
bool TimeToSet = false;
//Experimental
void * ztFileStream(Sint8 * filename, void * startAddress, entity_t * model, Sint8 evenStep, Sint8 oddStep)
{
void * currentAddress;
currentAddress = startAddress;
//Data of read structure
Sint32 fid = GFS_NameToId((Sint8*)filename);
GfsHn gfsx;
Sint32 nsct;
Sint32 fsize;
//Accepted rate of reading per frame (33 ms)
//Note to self: If your data is already byte size information, do not annotate with sizeOf. Thanks.
Sint32 rt_step = 2048;
Uint8 rt_sector = rt_step / 2048;
Uint8 synctime;
//Data of read-in-process
Sint32 stat;
Sint32 rdsize;
//Model Data is the destination of data in the work area.
modelData_t bufModelX;
//Destination address?
void * ptr2 = &bufModelX;
if(gfsx == NULL && file_reading != true){
//Loading follows
gfsx = GFS_Open(fid);
//Get sectors
//HEY! SBL DOCUMENTATION IS WRONG! THIRD ITEM NSCT IS gfsx SECTOR COUNT. SECOND ITEM IS CD SECTOR SIZE.
GFS_GetFileSize(gfsx, NULL, &nsct, NULL);
GFS_GetFileInfo(gfsx, NULL, NULL, &fsize, NULL);
//How many frames are we reading?
if(rt_step < fsize){
rd_frames = (fsize + (rt_step - 1))/(rt_step);
}
}
//Skip the loop if it is finished
if(curRdFrame >= rd_frames){
if(TimeToSet != true){
// Copy gfsx payload to modelData_t bufmodel
memcpy_l((Sint32*)ptr2, (Sint32*)(currentAddress), sizeof(modelData_t));
//ADDED
model->pos[X]=bufModelX.Origin[X]; model->pos[Y]=bufModelX.Origin[Y]; model->pos[Z]=bufModelX.Origin[Z];
model->length[X]=bufModelX.Length[X]; model->length[Y]=bufModelX.Length[Y]; model->length[Z]=bufModelX.Length[Z];
model->nbMeshes=bufModelX.TOTAL_MESH;
Uint16 first_texture = loadTextures(currentAddress, &bufModelX);
Sint32 bytesOff = bufModelX.TEXT_SIZE+(sizeof(modelData_t));
currentAddress = (void*)(currentAddress + bytesOff);
currentAddress = loadPDATA((void*)(currentAddress), model, &bufModelX);
currentAddress = (void*)(currentAddress + (bufModelX.PDATA_SIZE + sizeof(modelData_t)));
setTextures(first_texture, model, bufModelX.TOTAL_MESH, false);
TimeToSet = true;
}
file_reading = true;
jo_printf(0, 9, "(skipped loop)");
goto SKIP;
}
jo_printf(0, 8, "(%i)", nsct);
jo_printf(10, 8, "(sectors)");
jo_printf(0, 9, "(%i)", fsize);
jo_printf(10, 9, "(file size)");
//Read from CD to CD buffer
//CONCEPT: NwFread specifies only a fetch from CD buffer. If no such read has been specified, it will carry it out itself.
//However, if you specify a read from CD, repeats of the fetch command will only fetch from the CD buffer and will not give errors for data remainders.
//Now I understand how the program on SBL Document 1 Page 18 works. It is incomplete; they assume the program is a system callback, but it works.
//TIP: Since TMODE_CPU is the SH1 of the CD system, there is no reason to use anything else, as that CPU is otherwise entirely idle.
GFS_SetTmode(gfsx, GFS_TMODE_CPU);
//TIP: Setting GFS_GMODE_RESIDENT is a guaranteed crash.
GFS_SetGmode(gfsx, GFS_GMODE_ERASE);
GFS_SetReadPara(gfsx, rt_step);
GFS_SetTransPara(gfsx, rt_sector);
//TIP: GFS_Nw_CdRead cannot be checked for completion. Sorry!
GFS_NwCdRead(gfsx, fsize);
PUSHF:
synctime = jo_get_ticks();
jo_printf(0, 2, "x(%i)x", synctime);
GFS_NwFread(gfsx, rt_sector, (Uint32*)(currentAddress +(curRdFrame * rt_step)), rt_step);
READING:
GFS_NwExecOne(gfsx);
GFS_NwGetStat(gfsx, &stat, &rdsize);
jo_printf(0, 11, "(%i)", rdsize);
jo_printf(10, 11, "(fetched filesize)");
jo_printf(0, 15, "(%i)", curRdFrame);
jo_printf(4, 15, "(%i)", rd_frames);
if(stat != GFS_SVR_COMPLETED && rdsize < rt_step && synctime & 2){
goto READING;
}
//Remainder of file in comparison to rt_step [%] is handled, because we do not want to repeat the loop for the end of the data anyway. Do you understand?
//Technically, a file exactly divisible by rt_step will read-loop indefinitely. Watch out for that.
GFS_NwStop(gfsx);
if(rdsize == rt_step && synctime & 2 ){
curRdFrame = curRdFrame + 1;
goto PUSHF;
}
GFS_Close(gfsx);
SKIP:
jo_printf(0, 13, "(%i)", bufModelX.TOTAL_MESH);
jo_printf(3, 13, "(%i)", bufModelX.PDATA_SIZE);
jo_printf(10, 13, "(%i)", model->nbMeshes);
return currentAddress;
}
Okay, that's what I thought I should do at first since that's how Jo engine does it.
Now I am at a similar problem to before: the read data is getting corrupted if I interrupt the NwFread loop (and thus run the program without interruption).
I do happen to know that the Saturn should be able to manage about 240 bytes fetched each frame, and if I use that for rt_step, it will just crash (im assuming because the data is corrupted).
I don't know why the data is getting corrupted...
typedef struct
{
bool file_done;
void * dstAddress;
entity_t * tmodel;
Sint8 * filename;
} request;
Uint32 rd_frames = 0;
Uint32 curRdFrame = 0;
Uint16 FilRqNm;
bool TimeToSet = false;
bool process_requested;
request requests[4];
void ztFileRequest(Sint8 * name, void * startAddress, entity_t * model, Uint16 rqNum)
{
if(requests[rqNum].file_done == true){
process_requested = false;
goto SKIP;
}
requests[rqNum].filename = name;
requests[rqNum].dstAddress = startAddress;
requests[rqNum].tmodel = model;
FilRqNm = rqNum;
process_requested = true;
jo_printf(0, 0, "(started)");
SKIP:
if(process_requested == false){
jo_printf(0, 0, "(-ended-)");
}
}
//Experimental
void ztFileServer(void)
{
if(process_requested != true){
goto SKIP;
}
void * currentAddress;
currentAddress = requests[FilRqNm].dstAddress;
//Data of read structure
Sint32 fid = GFS_NameToId((Sint8*)requests[FilRqNm].filename);
GfsHn gfsx;
Sint32 nsct;
Sint32 fsize;
//Accepted rate of reading per frame (33 ms)
//Note to self: If your data is already byte size information, do not annotate with sizeOf. Thanks.
Sint32 rt_step = 2048;
Uint8 rt_sector = 1;
//Data of read-in-process
Sint32 stat;
Sint32 rdsize;
bool gfs_Active = false;
//Model Data is the destination of data in the work area.
modelData_t bufModelX;
//Destination address?
void * ptr2 = &bufModelX;
if(gfsx == NULL && requests[FilRqNm].file_done != true){
//Loading follows
gfsx = GFS_Open(fid);
gfs_Active = true;
//Get sectors
//HEY! SBL DOCUMENTATION IS WRONG! THIRD ITEM NSCT IS GFS SECTOR COUNT. SECOND ITEM IS CD SECTOR SIZE.
GFS_GetFileSize(gfsx, NULL, &nsct, NULL);
GFS_GetFileInfo(gfsx, NULL, NULL, &fsize, NULL);
//How many frames are we reading?
if(rt_step < fsize){
rd_frames = (fsize + (rt_step - 1))/(rt_step);
}
}
//Skip the loop if it is finished
if(curRdFrame >= rd_frames){
if(requests[FilRqNm].file_done != true){
// Copy gfsx payload to modelData_t bufmodel
memcpy_l((Sint32*)ptr2, (Sint32*)(currentAddress), sizeof(modelData_t));
//ADDED
requests[FilRqNm].tmodel->pos[X]=bufModelX.Origin[X]; requests[FilRqNm].tmodel->pos[Y]=bufModelX.Origin[Y]; requests[FilRqNm].tmodel->pos[Z]=bufModelX.Origin[Z];
requests[FilRqNm].tmodel->length[X]=bufModelX.Length[X]; requests[FilRqNm].tmodel->length[Y]=bufModelX.Length[Y]; requests[FilRqNm].tmodel->length[Z]=bufModelX.Length[Z];
requests[FilRqNm].tmodel->nbMeshes=bufModelX.TOTAL_MESH;
Uint16 first_texture = loadTextures(currentAddress, &bufModelX);
Sint32 bytesOff = bufModelX.TEXT_SIZE+(sizeof(modelData_t));
currentAddress = (void*)(currentAddress + bytesOff);
currentAddress = loadPDATA((void*)(currentAddress), requests[FilRqNm].tmodel, &bufModelX);
currentAddress = (void*)(currentAddress + (bufModelX.PDATA_SIZE + sizeof(modelData_t)));
setTextures(first_texture, requests[FilRqNm].tmodel, bufModelX.TOTAL_MESH, false);
}
requests[FilRqNm].file_done = true;
jo_printf(0, 9, "(skipped loop)");
goto SKIP;
}
jo_printf(0, 8, "(%i)", nsct);
jo_printf(10, 8, "(sectors)");
jo_printf(0, 9, "(%i)", fsize);
jo_printf(10, 9, "(file size)");
//Read from CD to CD buffer
//CONCEPT: NwFread specifies only a fetch from CD buffer. If no such read has been specified, it will carry it out itself.
//However, if you specify a read from CD, repeats of the fetch command will only fetch from the CD buffer and will not give errors for data remainders.
//TIP: Since TMODE_CPU is the SH1 of the CD system, there is no reason to use anything else, as that CPU is otherwise entirely idle.
GFS_SetTmode(gfsx, GFS_TMODE_CPU);
//TIP: Setting GFS_GMODE_RESIDENT is a guaranteed crash.
GFS_SetGmode(gfsx, GFS_GMODE_ERASE);
GFS_SetReadPara(gfsx, rt_step);
GFS_SetTransPara(gfsx, rt_sector);
//TIP: GFS_Nw_CdRead cannot be checked for completion. Sorry!
GFS_NwCdRead(gfsx, fsize);
PUSHF:
GFS_NwFread(gfsx, rt_sector, (Uint32*)(currentAddress +(curRdFrame * rt_step)), rt_step);
do{
GFS_NwExecOne(gfsx);
GFS_NwGetStat(gfsx, &stat, &rdsize);
jo_printf(0, 11, "(%i)", rdsize);
jo_printf(10, 11, "(fetched filesize)");
}while(stat != GFS_SVR_COMPLETED && rdsize < rt_step);
//Remainder of file in comparison to rt_step [%] is handled, because we do not want to repeat the loop for the end of the data anyway. Do you understand?
//Technically, a file exactly divisible by rt_step will read-loop indefinitely. Watch out for that.
if(rdsize == rt_step){
// GFS_NwStop(gfsx);
curRdFrame = curRdFrame + 1;
// goto PUSHF;
}
if(gfs_Active == true){
GFS_Close(gfsx);
gfs_Active = false;
}
SKIP:
jo_printf(0, 15, "(%i)", curRdFrame);
jo_printf(4, 15, "(%i)", rd_frames);
jo_printf(0, 13, "(%i)", bufModelX.TOTAL_MESH);
jo_printf(3, 13, "(%i)", bufModelX.PDATA_SIZE);
jo_printf(10, 13, "(%i)", requests[FilRqNm].tmodel->nbMeshes);
}
Here, this is the loading structure.
*30 FPS, by the way. My calculation of roughly 240 bytes per frame must have been at 60 FPS. It is important to know this is an extrapolation based on CDDA quality. ... it's probably wrong.
void load_test()
{
//Hesitant to add code here...
if(accelState > 0){
load_this++;
}
jo_printf(20, 0, "(%i)", load_this);
if(load_this < 1){
//This mesh is 216 quads
currentAddress = ztFileRequest("BIN",(Sint8*)"BPONY.ZTP", currentAddress, &entities[0], 0);
}
//Left turn steps
if(load_this > 60 && load_this < 119){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN01.ZTP", currentAddress, &entities[11], 1);
}
if(load_this > 120 && load_this < 179){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN02.ZTP", currentAddress, &entities[12], 2);
}
if(load_this > 180 && load_this < 239){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN03.ZTP", currentAddress, &entities[13], 3);
}
if(load_this > 240 && load_this < 379){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN04.ZTP", currentAddress, &entities[14], 4);
}
if(load_this > 380 && load_this < 479){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN05.ZTP", currentAddress, &entities[15], 5);
}
if(load_this > 540 && load_this < 599){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN06.ZTP", currentAddress, &entities[16], 6);
}
if(load_this > 660 && load_this < 729){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN07.ZTP", currentAddress, &entities[17], 7);
}
if(load_this > 780 && load_this < 839){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN08.ZTP", currentAddress, &entities[18], 8);
}
if(load_this > 900 && load_this < 959){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN09.ZTP", currentAddress, &entities[19], 9);
}
if(load_this > 999 && load_this < 1100){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN10.ZTP", currentAddress, &entities[20], 10);
}
}
Very simple. In a given range of numbers, the model loading code is active. I don't really need to do it that way, but I do.
This is the file request function.
void * ztFileRequest(char * dirname, Sint8 * name, void * startAddress, entity_t * model, Uint16 rqNum)
{
FilRqNm = rqNum;
if(requests[FilRqNm].file_done == true){
process_requested = false;
jo_printf(0, 0, "(-ended-)");
goto SKIP;
}
if(process_requested == false){
rd_frames = 0;
curRdFrame = 0;
ztCDsetRoot();
}
ztCDsetDir(dirname);
requests[FilRqNm].filename = GFS_NameToId(name);
requests[FilRqNm].dstAddress = startAddress;
requests[FilRqNm].tmodel = model;
process_requested = true;
jo_printf(0, 0, "(started)");
SKIP:
return requests[FilRqNm].dstAddress;
}
And this explains why the model is sometimes not visible. This was my animation system for the old ZTPs using a unique mesh for each frame.
void anim_entity_state(Uint16 start, Uint8 length, Sint8 sync, Bool loop, Bool reverse)
{
if(loop == true && reverse == false){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
anim_target -= length;
}
display_ztp(&entities[start+anim_target]);
}
if(loop == true && reverse == true){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
anim_target -= length;
}
display_ztp(&entities[start+length-anim_target]);
}
if(loop == false && reverse == false){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
display_ztp(&entities[start]);
}
display_ztp(&entities[start+anim_target]);
}
if(loop == false && reverse == true){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
display_ztp(&entities[start]);
}
display_ztp(&entities[start+length-anim_target]);
}
}
if(accelState == 0 && rotState[X] == 0){
anim_target = 0;
display_ztp(&entities[0]);
}
if(accelState > 0){
anim_entity_state(21, 19, evenFrame, true, false);
} else if(accelState < 0){
anim_entity_state(21, 19, evenFrame, true, true);
} else if(rotState[X] < 0){
anim_entity_state(1, 9, evenFrame, true, false);
} else if(rotState[X] > 0){
anim_entity_state(11, 9, evenFrame, true, false);
}
I'm wordy but I like to leave little up to question.
Once the mesh is finished loading, no more loading is done with it.
It is important to note that if you wanted to use that entity slot for a different mesh, you can just load to that mesh address (&entitites[number]) again and it will be replaced.
And finally, the core loading loop. Note that there is a void function parameter in the function This is where you put the game loop.
//Experimental
void ztFileServer(void(*sysframe)(void))
{
if(process_requested != true){
goto SKIP;
}
void * workAddress;
workAddress = requests[FilRqNm].dstAddress;
//Data of read structure
Sint32 fid = (Sint32)requests[FilRqNm].filename;
GfsHn gfsx;
Sint32 nzect;
Sint32 fsize;
//Accepted rate of reading per frame (33 ms)
//Note to self: If your data is already byte size information, do not annotate with sizeOf. Thanks.
Sint32 rt_step = 512;
Uint8 rt_sector = 1;
//Data of read-in-process
Sint32 stat;
Sint32 rdsize;
//Model Data is the destination of data in the work area.
modelData_t bufModelX;
//Destination address?
void * ptr2 = &bufModelX;
//Loading follows
gfsx = GFS_Open(fid);
//Get sectors
//HEY! SBL DOCUMENTATION IS WRONG! THIRD ITEM nzect IS GFS SECTOR COUNT. SECOND ITEM IS CD SECTOR SIZE.
GFS_GetFileSize(gfsx, NULL, &nzect, NULL);
GFS_GetFileInfo(gfsx, NULL, NULL, &fsize, NULL);
//How many frames are we reading?
if(rt_step < fsize){
rd_frames = (fsize + (rt_step - 1))/(rt_step);
}
//Skip the loop if it is finished
if(curRdFrame >= rd_frames){
if(requests[FilRqNm].file_done != true){
// Copy gfsx payload to modelData_t bufmodel
slDMACopy(workAddress, ptr2, sizeof(modelData_t));
slDMAWait();
//ADDED
requests[FilRqNm].tmodel->pos[X]=bufModelX.Origin[X];
requests[FilRqNm].tmodel->pos[Y]=bufModelX.Origin[Y];
requests[FilRqNm].tmodel->pos[Z]=bufModelX.Origin[Z];
requests[FilRqNm].tmodel->length[X]=bufModelX.Length[X];
requests[FilRqNm].tmodel->length[Y]=bufModelX.Length[Y];
requests[FilRqNm].tmodel->length[Z]=bufModelX.Length[Z];
requests[FilRqNm].tmodel->nbMeshes=bufModelX.TOTAL_MESH;
Uint16 first_texture = loadTextures(workAddress, &bufModelX);
Sint32 bytesOff = bufModelX.TEXT_SIZE+(sizeof(modelData_t));
workAddress = (workAddress + bytesOff);
workAddress = loadPDATA((workAddress), requests[FilRqNm].tmodel, &bufModelX);
workAddress = (workAddress + (bufModelX.PDATA_SIZE + sizeof(modelData_t)));
setTextures(first_texture, requests[FilRqNm].tmodel, bufModelX.TOTAL_MESH, true);
requests[FilRqNm].dstAddress = workAddress;
}
requests[FilRqNm].file_done = true;
jo_printf(0, 9, "(skipped loop)");
goto SKIP;
}
jo_printf(0, 8, "(%i)", nzect);
jo_printf(10, 8, "(sectors)");
jo_printf(0, 9, "(%i)", fsize);
jo_printf(10, 9, "(file size)");
//Read from CD to CD buffer
//CONCEPT: NwFread specifies only a fetch from CD buffer. If no such read has been specified, it will carry it out itself.
//However, if you specify a read from CD, repeats of the fetch command will only fetch from the CD buffer and will not give errors for data remainders.
GFS_SetReadPara(gfsx, rt_step);
GFS_SetTransPara(gfsx, rt_sector);
//TIP: GFS_Nw_CdRead cannot be checked for completion. Sorry!
GFS_NwCdRead(gfsx, fsize);
for( ; curRdFrame < rd_frames ; curRdFrame++){
GFS_NwFread(gfsx, rt_sector, (Uint32*)(workAddress +(curRdFrame * rt_step)), rt_step);
do{
GFS_NwExecOne(gfsx);
GFS_NwGetStat(gfsx, &stat, &rdsize);
jo_printf(0, 11, "(%i)", rdsize);
jo_printf(10, 11, "(fetched filesize)");
}while(stat != GFS_SVR_COMPLETED && rdsize < rt_step);
//Remainder of file in comparison to rt_step [%] is handled, because we do not want to repeat the loop for the end of the data anyway. Do you understand?
//Technically, a file exactly divisible by rt_step will read-loop indefinitely. Watch out for that.
sysframe();
slSynch();
jo_printf(0, 15, "(%i)", curRdFrame);
jo_printf(4, 15, "(%i)", rd_frames);
jo_printf(0, 13, "(%i)", bufModelX.TOTAL_MESH);
jo_printf(3, 13, "(%i)", bufModelX.PDATA_SIZE);
jo_printf(10, 13, "(%i)", requests[FilRqNm].tmodel->nbMeshes);
}
SKIP:
//It is important to ensure GFS is closed at all times outside the read loop.
GFS_Close(gfsx);
sysframe();
}