// get ready for some MLG... // These get four things pushed together: the address of what's being // changed, what type the data is, the original data, and the type again. // The type is encoded twice so we always know how big the data is whether // we're undoing or redoing. // We need to encode data that's able to go from A -> B as well as B -> A. Thus // for the ints I encode the difference instead of just whatever the last value // was. Strings currently just stores both. void Action_Change_Commit(memory *Memory, void *OriginalData, void *NewData, action_change_type ActionChange) { void *UndoEntry = AllocateMemory(Memory, sizeof(void *), P_UndoBuffer); *(ptrsize *)UndoEntry = (ptrsize)OriginalData; void *Data = AllocateMemory(Memory, sizeof(action_change_type), P_UndoBuffer); *(action_change_type *)Data = ActionChange; switch (ActionChange) { case action_change_u16: { uint16 OriginalValue = *(uint16 *)OriginalData; uint16 NewValue = *(uint16 *)NewData; uint16 Difference = NewValue - OriginalValue; void *Data = AllocateMemory(Memory, sizeof(uint16), P_UndoBuffer); *(uint16 *)Data = Difference; *(uint16 *)OriginalData = *(uint16 *)NewData; } break; case action_change_i16: { int16 OriginalValue = *(int16 *)OriginalData; int16 NewValue = *(int16 *)NewData; int16 Difference = NewValue - OriginalValue; void *Data = AllocateMemory(Memory, sizeof(int16), P_UndoBuffer); *(int16 *)Data = Difference; *(int16 *)OriginalData = *(int16 *)NewData; } break; case action_change_u32: { uint32 OriginalValue = *(uint32 *)OriginalData; uint32 NewValue = *(uint32 *)NewData; uint32 Difference = NewValue - OriginalValue; void *Data = AllocateMemory(Memory, sizeof(uint32), P_UndoBuffer); *(uint32 *)Data = Difference; *(uint32 *)OriginalData = *(uint32 *)NewData; } break; case action_change_i32: { int32 OriginalValue = *(int32 *)OriginalData; int32 NewValue = *(int32 *)NewData; int32 Difference = NewValue - OriginalValue; void *Data = AllocateMemory(Memory, sizeof(int32), P_UndoBuffer); *(int32 *)Data = Difference; *(int32 *)OriginalData = *(int32 *)NewData; } break; case action_change_r32: { real32 OriginalValue = *(real32 *)OriginalData; real32 NewValue = *(real32 *)NewData; real32 Difference = NewValue - OriginalValue; void *Data = AllocateMemory(Memory, sizeof(real32), P_UndoBuffer); *(real32 *)Data = Difference; *(real32 *)OriginalData = *(real32 *)NewData; } break; case action_change_u64: { uint64 OriginalValue = *(uint64 *)OriginalData; uint64 NewValue = *(uint64 *)NewData; uint64 Difference = NewValue - OriginalValue; void *Data = AllocateMemory(Memory, sizeof(uint64), P_UndoBuffer); *(uint64 *)Data = Difference; *(uint64 *)OriginalData = *(uint64 *)NewData; } break; case action_change_ptr: { ptrsize OriginalValue = *(ptrsize *)OriginalData; ptrsize NewValue = *(ptrsize *)NewData; ptrsize Difference = NewValue - OriginalValue; void *Data = AllocateMemory(Memory, sizeof(ptrsize), P_UndoBuffer); *(ptrsize *)Data = Difference; *(ptrsize *)OriginalData = *(ptrsize *)NewData; } break; case action_change_string: { void *Data = AllocateMemory(Memory, STRING_SIZE, P_UndoBuffer); CopyStrings(Data, OriginalData); Data = AllocateMemory(Memory, STRING_SIZE, P_UndoBuffer); CopyStrings(Data, NewData); CopyStrings(OriginalData, NewData); } break; } Data = AllocateMemory(Memory, sizeof(action_change_type), P_UndoBuffer); *(action_change_type *)Data = ActionChange; } // TODO(fox): Maybe take the extra lines to separate the incrementing of the // address from the variable assignment to improve legibility. // The pointer is unwinded. void Action_Undo(memory *Memory) { memory_table *Table = &Memory->Slot[P_UndoBuffer]; uint8 *LastPos = (uint8 *)Table->Address + Table->CurrentPosition; action_change_type *ActionType = (action_change_type *)LastPos - 1; uint8 *TableAddress; switch (*ActionType) { case action_change_u16: { uint16 *Difference = (uint16 *)ActionType - 1; TableAddress = (uint8 *)Difference - sizeof(ptrsize) - sizeof(action_change_type); void *Address = (void *)*(ptrsize *)TableAddress; *(uint16 *)Address -= *Difference; } break; case action_change_ptr: { ptrsize *Difference = (ptrsize *)ActionType - 1; TableAddress = (uint8 *)Difference - sizeof(ptrsize) - sizeof(action_change_type); void *Address = (void *)*(ptrsize *)TableAddress; *(ptrsize *)Address -= *Difference; } break; } Table->CurrentPosition = (TableAddress - (uint8 *)Table->Address); } // The pointer is rewinded. void Action_Redo(memory *Memory) { memory_table *Table = &Memory->Slot[P_UndoBuffer]; uint8 *LastPos = (uint8 *)Table->Address + Table->CurrentPosition; void *Address = (void *)*(ptrsize *)LastPos; action_change_type *ActionType = (action_change_type *)(LastPos + sizeof(void *)); uint8 *TableAddress; switch (*ActionType) { case action_change_u16: { uint16 *Difference = (uint16 *)(ActionType + 1); TableAddress = (uint8 *)Difference + sizeof(uint16) + sizeof(action_change_type); *(uint16 *)Address += *Difference; } break; case action_change_ptr: { ptrsize *Difference = (ptrsize *)(ActionType + 1); TableAddress = (uint8 *)Difference + sizeof(ptrsize) + sizeof(action_change_type); *(ptrsize *)Address += *Difference; } break; } Table->CurrentPosition = (TableAddress - (uint8 *)Table->Address); }