// get ready for some MLG... // The undo system currently works in two layers: a lower-level structless // stack that records "actions" and a higher-level array of "entries" that // bundle actions together. An action can either record a single change to a // specific memory address or a shift of pointers. Entries are what the user // sees and can contain multiple actions (i.e. adding a source changes the // value of the string and the increment of how many sources there are). // Entries are allowed to call functions in case there's something that can't // be incorporated into this memory model (i.e. deallocating libav contexts // when a layer's creation is undone), though they should be used only // when necessary. // These get four things pushed together: what type the data is, address, 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 History_Action_Change(memory *Memory, void *DataAddress, void *OriginalData, void *NewData, action_type ActionChange) { Memory->Action.Entry[Memory->Action.Index].NumberOfActions++; void *Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = ActionChange; void *UndoEntry = Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); *(ptrsize *)UndoEntry = (ptrsize)DataAddress; switch (ActionChange) { case action_type_change_u16: { uint16 OriginalValue = *(uint16 *)OriginalData; uint16 NewValue = *(uint16 *)NewData; uint16 Difference = NewValue - OriginalValue; void *Data = Memory_Advance(Memory, sizeof(uint16), P_UndoBuffer); *(uint16 *)Data = Difference; *(uint16 *)DataAddress = *(uint16 *)NewData; } break; case action_type_change_i16: { int16 OriginalValue = *(int16 *)OriginalData; int16 NewValue = *(int16 *)NewData; int16 Difference = NewValue - OriginalValue; void *Data = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); *(int16 *)Data = Difference; *(int16 *)DataAddress = *(int16 *)NewData; } break; case action_type_change_u32: { uint32 OriginalValue = *(uint32 *)OriginalData; uint32 NewValue = *(uint32 *)NewData; uint32 Difference = NewValue - OriginalValue; void *Data = Memory_Advance(Memory, sizeof(uint32), P_UndoBuffer); *(uint32 *)Data = Difference; *(uint32 *)DataAddress = *(uint32 *)NewData; } break; case action_type_change_i32: { int32 OriginalValue = *(int32 *)OriginalData; int32 NewValue = *(int32 *)NewData; int32 Difference = NewValue - OriginalValue; void *Data = Memory_Advance(Memory, sizeof(int32), P_UndoBuffer); *(int32 *)Data = Difference; *(int32 *)DataAddress = *(int32 *)NewData; } break; case action_type_change_r32: { real32 OriginalValue = *(real32 *)OriginalData; real32 NewValue = *(real32 *)NewData; real32 Difference = NewValue - OriginalValue; void *Data = Memory_Advance(Memory, sizeof(real32), P_UndoBuffer); *(real32 *)Data = Difference; *(real32 *)DataAddress = *(real32 *)NewData; } break; case action_type_change_u64: { uint64 OriginalValue = *(uint64 *)OriginalData; uint64 NewValue = *(uint64 *)NewData; uint64 Difference = NewValue - OriginalValue; void *Data = Memory_Advance(Memory, sizeof(uint64), P_UndoBuffer); *(uint64 *)Data = Difference; *(uint64 *)DataAddress = *(uint64 *)NewData; } break; case action_type_change_ptr: { ptrsize OriginalValue = *(ptrsize *)OriginalData; ptrsize NewValue = *(ptrsize *)NewData; ptrsize Difference = NewValue - OriginalValue; void *Data = Memory_Advance(Memory, sizeof(ptrsize), P_UndoBuffer); *(ptrsize *)Data = Difference; *(ptrsize *)DataAddress = *(ptrsize *)NewData; } break; case action_type_change_string: { void *Data = Memory_Advance(Memory, STRING_SIZE, P_UndoBuffer); CopyStrings(Data, OriginalData); Data = Memory_Advance(Memory, STRING_SIZE, P_UndoBuffer); CopyStrings(Data, NewData); CopyStrings(DataAddress, NewData); } break; default: { Assert(0); } } Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = ActionChange; } // Helper functions for common types of data changes. void History_Action_Change_SwapBool(memory *Memory, bool32 *Bool) { bool32 OppositeBool = *Bool ^ 1; History_Action_Change(Memory, Bool, Bool, &OppositeBool, action_type_change_i32); } void History_Action_Change_V2(memory *Memory, v2 *DataAddress, v2 *OriginalData, v2 *NewData) { History_Action_Change(Memory, (void *)&DataAddress->x, (void *)&OriginalData->x, (void *)&NewData->x, action_type_change_r32); History_Action_Change(Memory, (void *)&DataAddress->y, (void *)&OriginalData->y, (void *)&NewData->y, action_type_change_r32); } void History_Action_Change_Increment(memory *Memory, void *Data, action_type ActionChange) { switch (ActionChange) { case action_type_change_u16: { uint16 DataPlusOne = (*(uint16 *)Data) + 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_i16: { int16 DataPlusOne = (*(int16 *)Data) + 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_u32: { uint32 DataPlusOne = (*(uint32 *)Data) + 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_i32: { int32 DataPlusOne = (*(int32 *)Data) + 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_r32: { real32 DataPlusOne = (*(real32 *)Data) + 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_u64: { uint64 DataPlusOne = (*(uint64 *)Data) + 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; default: { Assert(0); } } } void History_Action_Change_Decrement(memory *Memory, void *Data, action_type ActionChange) { switch (ActionChange) { case action_type_change_u16: { uint16 DataPlusOne = (*(uint16 *)Data) - 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_i16: { int16 DataPlusOne = (*(int16 *)Data) - 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_u32: { uint32 DataPlusOne = (*(uint32 *)Data) - 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_i32: { int32 DataPlusOne = (*(int32 *)Data) - 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_r32: { real32 DataPlusOne = (*(real32 *)Data) - 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; case action_type_change_u64: { uint64 DataPlusOne = (*(uint64 *)Data) - 1; History_Action_Change(Memory, Data, Data, &DataPlusOne, ActionChange); } break; default: { Assert(0); } } } static void Arbitrary_ShiftData(action_shift_data ShiftData) { int16 StopAt = ShiftData.Index; if (ShiftData.Direction > 0) { int16 i = ShiftData.NumberOf - 1; while (i >= StopAt) { uint8 *CurrentData = (uint8 *)ShiftData.StartingAddress + (ShiftData.Size * i); uint8 *NextData = (uint8 *)ShiftData.StartingAddress + (ShiftData.Size * (i + ShiftData.Direction)); uint32 Bytes = 0; while (Bytes < ShiftData.Size) { *NextData++ = *CurrentData++; Bytes++; } i--; } } else { int16 i = StopAt; while (i <= ShiftData.NumberOf - 1) { uint8 *CurrentData = (uint8 *)ShiftData.StartingAddress + (ShiftData.Size * i); uint8 *NextData = (uint8 *)ShiftData.StartingAddress + (ShiftData.Size * (i - ShiftData.Direction)); uint32 Bytes = 0; while (Bytes < ShiftData.Size) { *CurrentData++ = *NextData++; Bytes++; } i++; } } } void History_Action_Shift_2(memory *Memory, void *StartingAddress, uint32 Size, uint16 NumberOf, int16 Direction, int16 Index) { Memory->Action.Entry[Memory->Action.Index].NumberOfActions++; void *Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = action_type_shift; action_shift_data *ShiftData = (action_shift_data *)Memory_Advance(Memory, sizeof(action_shift_data), P_UndoBuffer); ShiftData->StartingAddress = StartingAddress; ShiftData->Size = Size; ShiftData->NumberOf = NumberOf; ShiftData->Index = Index; ShiftData->Direction = Direction; Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = action_type_shift; Arbitrary_ShiftData(*ShiftData); } void History_Action_Shift(memory *Memory, action_type ActionChange, void *DataAddress, int16 Direction, int16 Index) { Memory->Action.Entry[Memory->Action.Index].NumberOfActions++; void *Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = ActionChange; switch (ActionChange) { case action_type_shift_keyframe: { void *DataPropertyAddress = Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); *(ptrsize *)DataPropertyAddress = (ptrsize)DataAddress; void *DataDirection = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); *(ptrsize *)DataDirection = (ptrsize)Direction; void *DataIndex = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); *(ptrsize *)DataIndex = (ptrsize)Index; } break; case action_type_shift_bezier: { void *DataPropertyAddress = Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); *(ptrsize *)DataPropertyAddress = (ptrsize)DataAddress; void *DataDirection = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); *(ptrsize *)DataDirection = (ptrsize)Direction; void *DataIndex = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); *(ptrsize *)DataIndex = (ptrsize)Index; } break; default: { Assert(0); } break; } Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = ActionChange; } // Overwrite area with arbitrary-sized memory. Only use if the previous data // doesn't need to be recorded, like pushing to the end of a stack. void History_Action_StoreData(memory *Memory, void *DataAddress, uint64 ByteSize) { Memory->Action.Entry[Memory->Action.Index].NumberOfActions++; void *Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = action_type_storedata; Data = Memory_Advance(Memory, sizeof(uint64), P_UndoBuffer); *(uint64 *)Data = ByteSize; Data = Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); *(ptrsize *)Data = (ptrsize)DataAddress; uint64 i = 0; while (i < ByteSize) { void *DataIndex = Memory_Advance(Memory, 1, P_UndoBuffer); *(uint8 *)DataIndex = *((uint8 *)DataAddress + i++); } Data = Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); *(ptrsize *)Data = (ptrsize)DataAddress; Data = Memory_Advance(Memory, sizeof(uint64), P_UndoBuffer); *(uint64 *)Data = ByteSize; Data = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); *(action_type *)Data = action_type_storedata; } // This is only called when we're certain the action is going to be taken. void History_Entry_Commit(memory *Memory, action_entry_type Type, char *Name) { // We need to at least clear NumberOfActions in case this index is being reused. Memory->Action.Entry[Memory->Action.Index] = {}; Memory->Action.Entry[Memory->Action.Index].Name = Name; Memory->Action.Entry[Memory->Action.Index].Type = Type; // Effectively deletes entries in front if we're beginning out of an undo. // It wouldn't be all that much more difficult to support branched undoing // now that I think about it... (would anyone use it though?) if (Memory->Action.Index != Memory->Action.NumberOfEntries) { Memory->Action.NumberOfEntries = Memory->Action.Index; } } void History_Entry_SetPointer(memory *Memory, void *Data) { Memory->Action.Entry[Memory->Action.Index].ExtraPointer = Data; } void History_Entry_End(memory *Memory) { Memory->Action.Index++; Memory->Action.NumberOfEntries++; } // The pointer is unwinded. void History_Action_Undo(memory *Memory) { void *LastPos = Memory_Rewind(Memory, sizeof(action_type), P_UndoBuffer); action_type *ActionType = (action_type *)LastPos; switch (*ActionType) { case action_type_change_u16: { uint16 *Difference = (uint16 *)Memory_Rewind(Memory, sizeof(uint16), P_UndoBuffer); void **Address = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); *(uint16 *)*Address -= *Difference; } break; case action_type_change_i16: { int16 *Difference = (int16 *)Memory_Rewind(Memory, sizeof(int16), P_UndoBuffer); void **Address = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); *(int16 *)*Address -= *Difference; } break; case action_type_change_u32: { uint32 *Difference = (uint32 *)Memory_Rewind(Memory, sizeof(uint32), P_UndoBuffer); void **Address = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); *(uint32 *)*Address -= *Difference; } break; case action_type_change_i32: { int32 *Difference = (int32 *)Memory_Rewind(Memory, sizeof(int32), P_UndoBuffer); void **Address = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); *(int32 *)*Address -= *Difference; } break; case action_type_change_r32: { real32 *Difference = (real32 *)Memory_Rewind(Memory, sizeof(real32), P_UndoBuffer); void **Address = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); *(real32 *)*Address -= *Difference; } break; case action_type_change_u64: { uint64 *Difference = (uint64 *)Memory_Rewind(Memory, sizeof(uint64), P_UndoBuffer); void **Address = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); *(uint64 *)*Address -= *Difference; } break; case action_type_change_ptr: { ptrsize *Difference = (ptrsize *)Memory_Rewind(Memory, sizeof(ptrsize), P_UndoBuffer); void **Address = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); *(ptrsize *)*Address -= *Difference; } break; case action_type_change_string: { Assert(0); } break; case action_type_shift_keyframe: { void *DataIndex = Memory_Rewind(Memory, sizeof(int16), P_UndoBuffer); void *DataDirection = Memory_Rewind(Memory, sizeof(int16), P_UndoBuffer); void **DataPropertyAddress = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); Keyframe_ShiftPointers((property_channel *)*DataPropertyAddress, *(int16 *)DataDirection * -1, *(int16 *)DataIndex); } break; case action_type_shift_bezier: { void *DataIndex = Memory_Rewind(Memory, sizeof(int16), P_UndoBuffer); void *DataDirection = Memory_Rewind(Memory, sizeof(int16), P_UndoBuffer); void **DataPropertyAddress = (void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); Mask_ShiftPointers((mask *)*DataPropertyAddress, *(int16 *)DataDirection * -1, *(int16 *)DataIndex); } break; case action_type_shift: { action_shift_data ShiftData = *(action_shift_data *)Memory_Rewind(Memory, sizeof(action_shift_data), P_UndoBuffer); ShiftData.Direction *= -1; Arbitrary_ShiftData(ShiftData); } break; case action_type_storedata: { uint64 *ByteSize = (uint64 *)Memory_Rewind(Memory, sizeof(uint64), P_UndoBuffer); void *DataAddress = *(void **)Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); void *DataStart = Memory_Rewind(Memory, *ByteSize, P_UndoBuffer); uint64 i = 0; while (i < *ByteSize) { void *DataIndex = Memory_GetAddressAt(Memory, i, P_UndoBuffer); *((uint8 *)DataAddress + i++) = *(uint8 *)DataIndex; } DataStart = Memory_Rewind(Memory, sizeof(void *), P_UndoBuffer); DataStart = Memory_Rewind(Memory, sizeof(uint64), P_UndoBuffer); } break; default: { Assert(0); } break; } void *EndPos = Memory_Rewind(Memory, sizeof(action_type), P_UndoBuffer); } // The pointer is rewinded. void History_Action_Redo(memory *Memory) { void *LastPos = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); action_type *ActionType = (action_type *)LastPos; switch (*ActionType) { case action_type_change_u16: { void **Address = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); uint16 *Difference = (uint16 *)Memory_Advance(Memory, sizeof(uint16), P_UndoBuffer); *(uint16 *)*Address += *Difference; } break; case action_type_change_i16: { void **Address = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); int16 *Difference = (int16 *)Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); *(int16 *)*Address += *Difference; } break; case action_type_change_u32: { void **Address = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); uint32 *Difference = (uint32 *)Memory_Advance(Memory, sizeof(uint32), P_UndoBuffer); *(uint32 *)*Address += *Difference; } break; case action_type_change_i32: { void **Address = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); int32 *Difference = (int32 *)Memory_Advance(Memory, sizeof(int32), P_UndoBuffer); *(int32 *)*Address += *Difference; } break; case action_type_change_r32: { void **Address = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); real32 *Difference = (real32 *)Memory_Advance(Memory, sizeof(real32), P_UndoBuffer); *(real32 *)*Address += *Difference; } break; case action_type_change_u64: { void **Address = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); uint64 *Difference = (uint64 *)Memory_Advance(Memory, sizeof(uint64), P_UndoBuffer); *(uint64 *)*Address += *Difference; } break; case action_type_change_ptr: { void **Address = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); ptrsize *Difference = (ptrsize *)Memory_Advance(Memory, sizeof(ptrsize), P_UndoBuffer); *(ptrsize *)*Address += *Difference; } break; case action_type_change_string: { Assert(0); } break; case action_type_shift_keyframe: { void **DataPropertyAddress = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); void *DataDirection = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); void *DataIndex = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); Keyframe_ShiftPointers((property_channel *)*DataPropertyAddress, *(int16 *)DataDirection, *(int16 *)DataIndex); } break; case action_type_shift_bezier: { void **DataPropertyAddress = (void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); void *DataDirection = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); void *DataIndex = Memory_Advance(Memory, sizeof(int16), P_UndoBuffer); Mask_ShiftPointers((mask *)*DataPropertyAddress, *(int16 *)DataDirection, *(int16 *)DataIndex); } break; case action_type_shift: { action_shift_data ShiftData = *(action_shift_data *)Memory_Advance(Memory, sizeof(action_shift_data), P_UndoBuffer); Arbitrary_ShiftData(ShiftData); } break; case action_type_storedata: { // NOTE(fox): Right now I'm only using this to overwrite data, so all we need to do is advance. uint64 *ByteSize = (uint64 *)Memory_Advance(Memory, sizeof(uint64), P_UndoBuffer); void *DataAddress = *(void **)Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); void *DataEnd = Memory_Advance(Memory, *ByteSize, P_UndoBuffer); DataEnd = Memory_Advance(Memory, sizeof(void *), P_UndoBuffer); DataEnd = Memory_Advance(Memory, sizeof(uint64), P_UndoBuffer); } break; default: { Assert(0); } break; } void *EndPos = Memory_Advance(Memory, sizeof(action_type), P_UndoBuffer); } void History_Undo(memory *Memory) { if (Memory->Action.Index != 0) { Memory->Action.Index--; action_entry Entry = Memory->Action.Entry[Memory->Action.Index]; switch (Entry.Type) { case action_entry_layerinit: { AV_Dealloc((av_info *)*(ptrsize *)Entry.ExtraPointer); *(ptrsize *)Entry.ExtraPointer = 0x0; // what actually dereferences the pointer } break; case action_entry_default: { } break; } for (int i = 0; i < Entry.NumberOfActions; i++) History_Action_Undo(Memory); } } void History_Redo(memory *Memory) { if (Memory->Action.Index != Memory->Action.NumberOfEntries) { action_entry Entry = Memory->Action.Entry[Memory->Action.Index]; switch (Entry.Type) { case action_entry_layerinit: { } break; case action_entry_default: { } break; } for (int i = 0; i < Entry.NumberOfActions; i++) History_Action_Redo(Memory); Memory->Action.Index++; } }