Task Queue System

From Wulinshu
Jump to navigation Jump to search

Task memory structure

Position Length Type Description
0x00 0x08 uint64_t* Callback function pointer
0x08 0x04 char* Task type/name token
0x0C 0x01 uint8_t Unknown value
0x0D 0x01 uint8_t Unknown value
0x10 0x08 uint64_t* Next task pointer
0x18 0x08 uint64_t* Some task pointer
0x20 0x08 uint64_t* Current task pointer (at the moment of creation)
0x28 0x08 uint64_t* Some task pointer
0x30 0x08 uint64_t* Some task pointer
0x38 0x08 uint64_t* Some task pointer
0x40 0x08 uint64_t* Some task pointer
0x48 0x08 uint64_t* Unknown pointer
0x50 0x08 uint64_t* Unknown pointer
0x58 0x08 uint64_t* Unknown pointer
0x60 0x08 uint64_t* Unknown pointer
0x68 0x08 uint64_t* Callback function parameter struct pointer

Task queue

Task Queue Pointer Offset: 0x1481FFA50 (v1.07)
At the start an ROOT task is created.
When the ROOT task was created the Queue gets filled with NONE task's which do nothing.
At initialization each task has the next task in order as their next task specified inside 0x18.
The last task however points to 0x142A88530 (v1.07) as their next task.
The ROOT task points to the first task as it's next task.

Enqueue task

Enqueue task without TASK data allocation

To enqueue an task without TASK data allocation the subroutine 0x14049D890 (v1.07) needs to be called.

 EnqueueTask(void* callbackFunction, uint8_t nextFunctionIndex, uint8_t a3, uint32_t taskToken)

Enqueue task with TASK data allocation

To enqueue an task with TASK data allocation the subroutine 0x14049DA10 (v1.07) needs to be called.

 EnqueueTaskWithData(void* callbackFunction, uint8_t nextFunctionIndex, uint8_t a3, uint32_t dataSize, uint32_t taskToken, char* debugTaskName)

Task creation

During the enqueue process the task will be created with the given callback function.
The callback function parameter, which is optional, is an pointer to an struct than can have any shape or size the callback function needs.
This parameter struct will be allocated inside the USE/fREe storage.

Task execution

The main loop iterates through the task queue and calls the callback function with the optional parameter.
When the callback function returns the next task pointer is read from the current task and moved into the task queue address pointer.

void Shenmue::Main(int argc, void *argv) {
  // initialize
  if ( Shenmue::Initialization(argc, argv) )
  {
    HLib_Task * taskPtr = nullptr;

    // loop all tasks
    for ( taskPtr = HLib::CurrentTask; ; HLib::CurrentTask = taskPtr )
    {
      // execute task function
      taskPtr->taskCallbackFnPtr(taskPtr->task_data);

      // select next task
      taskPtr = HLib::CurrentTask->next_task;
    }
  }
}

Task cleanup (Destruction)

A task controls how long it lives in its own callback function.
When a task wants to be destroyed it calls the subroutine 0x14049D660 (v1.07) during its own callback.

Tasks can also be destroyed if a pointer can be retrieved for it, like this:

  HLib_Task * TASK = HLib::EnqueueTaskWithoutParameter(HLTaskFunc_CharacterHandler_Callback, a1, 4ui64, 'RAHC');
  if ( !TASK )
    goto COULD_NOT_ENQUEUE_TASK;
  if ( TASK->task_data )
    goto TASK_DATA_ALREADY_EXISTS;

  task_memory = HLib::AllocHeapMemBlock(272i64, 'KSAT');
  if ( task_memory )
    *((int *)task_memory + 0x7) |= 0x10u;

  TASK->task_data = task_memory;
  if ( !task_memory )
  {
TASK_DATA_ALREADY_EXISTS:
    TASK->initTaskFnPtr = 0i64;
    TASK->taskCallbackFnPtr = HLib::DestroyCurrentTask;
    TASK->taskPtr08 = 0i64;
COULD_NOT_ENQUEUE_TASK:
    TASK = 0i64;
  }

Task parameter

When a task gets enqueued with a parameter it will need to find some "fREe" storage in the USE/fREe storage.

Storage entry

Position Length Type Description
0x00 0x04 string Task token (TASK)
0x10 0x08 uint64_t* Unknown pointer
0x18 0x08 uint64_t* Unknown pointer
0x20 0x04 string USE/fREe token
0x40 0x?? ? Storage data