Anton Tarasenko
2 years ago
28 changed files with 1612 additions and 5 deletions
@ -0,0 +1,277 @@ |
|||||||
|
/** |
||||||
|
* Author: dkanus |
||||||
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore |
||||||
|
* License: GPL |
||||||
|
* Copyright 2022-2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class SideEffect extends AcediaObject; |
||||||
|
|
||||||
|
//! Defines the concept of "side effects" in the context of the Acedia and its derivative mods. |
||||||
|
//! |
||||||
|
//! Side effects are changes that are not part of the mod's main functionality, but rather something |
||||||
|
//! necessary to enable that functionality, while also possibly affecting how other mods work. |
||||||
|
//! Documenting these side effects helps developers and server admins understand changes performed |
||||||
|
//! by Acedia or mods based on it, and anticipate any potential conflicts or issues that may arise. |
||||||
|
//! |
||||||
|
//! It should be noted that what constitutes a side effect is loosely defined, and it is simply |
||||||
|
//! a tool to inform others that something unexpected has happened, possibly breaking other mods. |
||||||
|
//! AcediaCore aims to leave a minimal footprint, but still needs to make some changes |
||||||
|
//! (e.g., adding GameRules, patching code of some functions), and [`SideEffects`] can be used to |
||||||
|
//! document them. |
||||||
|
//! Similarly, [`SideEffect`]s can be used to document changes made by AcediaFixes, a package meant |
||||||
|
//! only for fixing bugs that inevitably needs to make many under-the-hood changes to achieve |
||||||
|
//! that goal. |
||||||
|
//! |
||||||
|
//! On the other hand gameplay mods like Futility or Ire can make a lot of changes, but they can all |
||||||
|
//! be just expected part of its direct functionality: we expect feature that shares dosh of leavers |
||||||
|
//! to alter players' dosh values, so this is not a side effect. |
||||||
|
//! Such mods are likely not going to have to specify any side effects whatsoever. |
||||||
|
|
||||||
|
var private Text name; |
||||||
|
var private Text description; |
||||||
|
var private Text package; |
||||||
|
var private Text source; |
||||||
|
var private Text status; |
||||||
|
var private bool initialized; |
||||||
|
|
||||||
|
protected function Finalizer() { |
||||||
|
_.memory.Free(name); |
||||||
|
_.memory.Free(description); |
||||||
|
_.memory.Free(package); |
||||||
|
_.memory.Free(source); |
||||||
|
_.memory.Free(status); |
||||||
|
name = none; |
||||||
|
description = none; |
||||||
|
package = none; |
||||||
|
source = none; |
||||||
|
status = none; |
||||||
|
initialized = false; |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks whether caller [`SideEffect`] was initialized. |
||||||
|
/// |
||||||
|
/// Initialization must happen directly after creation and only initialized instances should |
||||||
|
/// ever be used. |
||||||
|
public final function bool IsInitialized() { |
||||||
|
return initialized; |
||||||
|
} |
||||||
|
|
||||||
|
/// This function is used to set the initial values of the [`SideEffect`] object properties when it |
||||||
|
/// is first created. |
||||||
|
/// |
||||||
|
/// All arguments must be not `none`. |
||||||
|
/// |
||||||
|
/// Returns `true` if the initialization was successful, `false` otherwise (including the case where |
||||||
|
/// the [`SideEffect`] object has already been initialized). |
||||||
|
public final function bool Initialize( |
||||||
|
BaseText sideEffectName, |
||||||
|
BaseText sideEffectDescription, |
||||||
|
BaseText sideEffectPackage, |
||||||
|
BaseText sideEffectSource, |
||||||
|
BaseText sideEffectStatus |
||||||
|
) { |
||||||
|
if (initialized) return false; |
||||||
|
if (sideEffectName == none) return false; |
||||||
|
if (sideEffectDescription == none) return false; |
||||||
|
if (sideEffectPackage == none) return false; |
||||||
|
if (sideEffectSource == none) return false; |
||||||
|
if (sideEffectStatus == none) return false; |
||||||
|
|
||||||
|
name = sideEffectName.Copy(); |
||||||
|
description = sideEffectDescription.Copy(); |
||||||
|
package = sideEffectPackage.Copy(); |
||||||
|
source = sideEffectSource.Copy(); |
||||||
|
status = sideEffectStatus.Copy(); |
||||||
|
initialized = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// This function is used to set the initial values of the [`SideEffect`] object properties when it |
||||||
|
/// is first created. |
||||||
|
/// |
||||||
|
/// Returns `true` if the initialization was successful, `false` otherwise (including the case where |
||||||
|
/// the [`SideEffect`] object has already been initialized). |
||||||
|
public final function bool Initialize_S( |
||||||
|
string sideEffectName, |
||||||
|
string sideEffectDescription, |
||||||
|
string sideEffectPackage, |
||||||
|
string sideEffectSource, |
||||||
|
string sideEffectStatus |
||||||
|
) { |
||||||
|
name = _.text.FromString(sideEffectName); |
||||||
|
description = _.text.FromString(sideEffectDescription); |
||||||
|
package = _.text.FromString(sideEffectPackage); |
||||||
|
source = _.text.FromString(sideEffectSource); |
||||||
|
status = _.text.FromString(sideEffectStatus); |
||||||
|
initialized = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a brief summary that conveys the purpose of the caller [SideEffect] to the user in |
||||||
|
/// a clear and concise manner. |
||||||
|
/// |
||||||
|
/// While there is no hard limit on the length of this value, it is recommended to keep it under 80 |
||||||
|
/// characters for readability. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetName() { |
||||||
|
if (initialized) { |
||||||
|
return name.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a brief summary that conveys the purpose of the caller [SideEffect] to the user in |
||||||
|
/// a clear and concise manner. |
||||||
|
/// |
||||||
|
/// While there is no hard limit on the length of this value, it is recommended to keep it under 80 |
||||||
|
/// characters for readability. |
||||||
|
public final function string GetName_S() { |
||||||
|
if (initialized && name != none) { |
||||||
|
return name.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the detailed description of the caller [`SideEffect`], which describes what was done |
||||||
|
/// and why the relevant change was necessary. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetDescription() { |
||||||
|
if (initialized) { |
||||||
|
return description.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the detailed description of the caller [`SideEffect`], which describes what was done |
||||||
|
/// and why the relevant change was necessary. |
||||||
|
public final function string GetDescription_S() { |
||||||
|
if (initialized && description != none) { |
||||||
|
return description.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the name of the package ("*.u" file) that introduced the changes |
||||||
|
/// represented by the caller `SideEffect`. |
||||||
|
/// |
||||||
|
/// It should be noted that even if a different package requested the functionality that led to |
||||||
|
/// the changes being made, the package responsible for the side effect is the one that performed |
||||||
|
/// the changes. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetPackage() { |
||||||
|
if (initialized) { |
||||||
|
return package.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the name of the package ("*.u" file) that introduced the changes |
||||||
|
/// represented by the caller `SideEffect`. |
||||||
|
/// |
||||||
|
/// It should be noted that even if a different package requested the functionality that led to |
||||||
|
/// the changes being made, the package responsible for the side effect is the one that performed |
||||||
|
/// the changes. |
||||||
|
public final function string GetPackage_S() { |
||||||
|
if (initialized && package != none) { |
||||||
|
return package.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// The origin of this change within the package is specified, and for larger packages, additional |
||||||
|
/// details can be provided to clarify the cause of the change. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetSource() { |
||||||
|
if (initialized) { |
||||||
|
return source.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// The origin of this change within the package is specified, and for larger packages, additional |
||||||
|
/// details can be provided to clarify the cause of the change. |
||||||
|
public final function string GetSource_S() { |
||||||
|
if (initialized && source != none) { |
||||||
|
return source.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// The status of the caller [`SideEffect`], that is used to differentiate between different ways |
||||||
|
/// that a side effect may have been introduced, allowing for better tracking and management of |
||||||
|
/// the effect. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetStatus() { |
||||||
|
if (initialized) { |
||||||
|
return status.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// The status of the caller [`SideEffect`], that is used to differentiate between different ways |
||||||
|
/// that a side effect may have been introduced, allowing for better tracking and management of |
||||||
|
/// the effect. |
||||||
|
public final function string GetStatus_S() { |
||||||
|
if (initialized && status != none) { |
||||||
|
return status.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
public function bool IsEqual(Object other) { |
||||||
|
local SideEffect otherSideEffect; |
||||||
|
|
||||||
|
if (self == other) return true; |
||||||
|
otherSideEffect = SideEffect(other); |
||||||
|
if (otherSideEffect == none) return false; |
||||||
|
if (!otherSideEffect.initialized) return false; |
||||||
|
if (GetHashCode() != otherSideEffect.GetHashCode()) return false; |
||||||
|
if (!name.Compare(otherSideEffect.name,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!package.Compare(otherSideEffect.package,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!source.Compare(otherSideEffect.source,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!status.Compare(otherSideEffect.status,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!description.Compare(otherSideEffect.description,, SFORM_SENSITIVE)) return false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
protected function int CalculateHashCode() { |
||||||
|
local int result; |
||||||
|
|
||||||
|
if (initialized) { |
||||||
|
result = name.GetHashCode(); |
||||||
|
result = CombineHash(result, description.GetHashCode()); |
||||||
|
result = CombineHash(result, package.GetHashCode()); |
||||||
|
result = CombineHash(result, source.GetHashCode()); |
||||||
|
result = CombineHash(result, status.GetHashCode()); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,194 @@ |
|||||||
|
/** |
||||||
|
* Author: dkanus |
||||||
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore |
||||||
|
* License: GPL |
||||||
|
* Copyright 2022-2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class SideEffectAPI extends AcediaObject; |
||||||
|
|
||||||
|
var private array<SideEffect> activeSideEffects; |
||||||
|
|
||||||
|
/// Returns an array containing all SideEffect objects that have been registered up to this point. |
||||||
|
/// |
||||||
|
/// The order of the elements in the array is not guaranteed. |
||||||
|
public function array<SideEffect> GetAll() { |
||||||
|
local int i; |
||||||
|
|
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
activeSideEffects[i].NewRef(); |
||||||
|
} |
||||||
|
return activeSideEffects; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns all registered [`SideEffects`] that are associated with the specified package name |
||||||
|
/// (case-insensitive). |
||||||
|
public function array<SideEffect> GetFromPackage(BaseText packageName) { |
||||||
|
local int i; |
||||||
|
local Text nextPackage; |
||||||
|
local array<SideEffect> result; |
||||||
|
|
||||||
|
if (packageName == none) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
nextPackage = activeSideEffects[i].GetPackage(); |
||||||
|
if (packageName.Compare(nextPackage, SCASE_INSENSITIVE)) { |
||||||
|
activeSideEffects[i].NewRef(); |
||||||
|
result[result.length] = activeSideEffects[i]; |
||||||
|
} |
||||||
|
_.memory.Free(nextPackage); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Adds a new side effect to the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if any of its arguments are `none` or a side effect with that exact |
||||||
|
/// contents was already added. |
||||||
|
public function SideEffect Add( |
||||||
|
BaseText sideEffectName, |
||||||
|
BaseText sideEffectDescription, |
||||||
|
BaseText sideEffectPackage, |
||||||
|
BaseText sideEffectSource, |
||||||
|
BaseText sideEffectStatus |
||||||
|
) { |
||||||
|
local bool initialized; |
||||||
|
local SideEffect newSideEffect; |
||||||
|
|
||||||
|
newSideEffect = SideEffect(_.memory.Allocate(class'SideEffect')); |
||||||
|
initialized = newSideEffect.Initialize( |
||||||
|
sideEffectName, |
||||||
|
sideEffectDescription, |
||||||
|
sideEffectPackage, |
||||||
|
sideEffectSource, |
||||||
|
sideEffectStatus); |
||||||
|
if (initialized) { |
||||||
|
if (!AddInstance(newSideEffect)) { |
||||||
|
_.memory.Free(newSideEffect); |
||||||
|
return none; |
||||||
|
} |
||||||
|
} else { |
||||||
|
_.memory.Free(newSideEffect); |
||||||
|
return none; |
||||||
|
} |
||||||
|
return newSideEffect; |
||||||
|
} |
||||||
|
|
||||||
|
/// Adds a new side effect to the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if a side effect with that exact contents was already added. |
||||||
|
public function SideEffect Add_S( |
||||||
|
string sideEffectName, |
||||||
|
string sideEffectDescription, |
||||||
|
string sideEffectPackage, |
||||||
|
string sideEffectSource, |
||||||
|
string sideEffectStatus |
||||||
|
) { |
||||||
|
local bool initialized; |
||||||
|
local SideEffect newSideEffect; |
||||||
|
|
||||||
|
newSideEffect = SideEffect(_.memory.Allocate(class'SideEffect')); |
||||||
|
initialized = newSideEffect.Initialize_S( |
||||||
|
sideEffectName, |
||||||
|
sideEffectDescription, |
||||||
|
sideEffectPackage, |
||||||
|
sideEffectSource, |
||||||
|
sideEffectStatus); |
||||||
|
if (initialized) { |
||||||
|
if (!AddInstance(newSideEffect)) { |
||||||
|
_.memory.Free(newSideEffect); |
||||||
|
return none; |
||||||
|
} |
||||||
|
} else { |
||||||
|
return none; |
||||||
|
} |
||||||
|
return newSideEffect; |
||||||
|
} |
||||||
|
|
||||||
|
/// Adds a new side effect to the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if its argument is `none`, non-initialized or a side effect with that |
||||||
|
/// exact contents was already added. |
||||||
|
public function bool AddInstance(SideEffect newSideEffect) { |
||||||
|
local int i; |
||||||
|
|
||||||
|
if (newSideEffect == none) return false; |
||||||
|
if (!newSideEffect.IsInitialized()) return false; |
||||||
|
|
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
if (activeSideEffects[i].IsEqual(newSideEffect)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
newSideEffect.NewRef(); |
||||||
|
activeSideEffects[activeSideEffects.length] = newSideEffect; |
||||||
|
LogAddingSideEffectChange(newSideEffect, true); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// Removes a side effect from the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if its argument is `none`, non-initialized or a side effect with its |
||||||
|
/// contents isn't in the records. |
||||||
|
public function bool RemoveInstance(SideEffect inactiveSideEffect) { |
||||||
|
local int i; |
||||||
|
local bool foundInstance; |
||||||
|
|
||||||
|
if (inactiveSideEffect == none) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
if (activeSideEffects[i].IsEqual(inactiveSideEffect)) { |
||||||
|
LogAddingSideEffectChange(activeSideEffects[i], false); |
||||||
|
_.memory.Free(activeSideEffects[i]); |
||||||
|
activeSideEffects.Remove(i, 1); |
||||||
|
foundInstance = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return foundInstance; |
||||||
|
} |
||||||
|
|
||||||
|
private function LogAddingSideEffectChange(SideEffect effect, bool added) { |
||||||
|
local MutableText builder; |
||||||
|
local Text sideEffectData; |
||||||
|
|
||||||
|
if (effect == none) { |
||||||
|
return; |
||||||
|
} |
||||||
|
builder = _.text.Empty(); |
||||||
|
if (added) { |
||||||
|
builder.Append(P("NEW SIDE EFFECT: ")); |
||||||
|
} else { |
||||||
|
builder.Append(P("REMOVED SIDE EFFECT: ")); |
||||||
|
} |
||||||
|
sideEffectData = effect.GetName(); |
||||||
|
builder.Append(sideEffectData); |
||||||
|
_.memory.Free(sideEffectData); |
||||||
|
sideEffectData = effect.GetStatus(); |
||||||
|
if (sideEffectData != none) { |
||||||
|
builder.Append(P(" {")); |
||||||
|
builder.Append(sideEffectData); |
||||||
|
_.memory.Free(sideEffectData); |
||||||
|
builder.Append(P("}")); |
||||||
|
} |
||||||
|
_.logger.Info(builder); |
||||||
|
builder.FreeSelf(); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class FunctionReplacement extends AcediaObject; |
||||||
|
|
||||||
|
var private Text replaced; |
||||||
|
var private Text replacer; |
||||||
|
var private SideEffect effect; |
||||||
|
|
||||||
|
protected function Finalizer() { |
||||||
|
_.memory.Free(replaced); |
||||||
|
_.memory.Free(replacer); |
||||||
|
_.memory.Free(effect); |
||||||
|
replaced = none; |
||||||
|
replacer = none; |
||||||
|
effect = none; |
||||||
|
} |
||||||
|
|
||||||
|
public static final function FunctionReplacement Make( |
||||||
|
BaseText oldFunction, |
||||||
|
BaseText newFunction, |
||||||
|
SideEffect sideEffect |
||||||
|
) { |
||||||
|
local FunctionReplacement newReplacement; |
||||||
|
|
||||||
|
if (oldFunction == none) return none; |
||||||
|
if (newFunction == none) return none; |
||||||
|
if (sideEffect == none) return none; |
||||||
|
|
||||||
|
newReplacement = FunctionReplacement(__().memory.Allocate(class'FunctionReplacement')); |
||||||
|
newReplacement.replaced = oldFunction.Copy(); |
||||||
|
newReplacement.replacer = newFunction.Copy(); |
||||||
|
sideEffect.NewRef(); |
||||||
|
newReplacement.effect = sideEffect; |
||||||
|
return newReplacement; |
||||||
|
} |
||||||
|
|
||||||
|
public final function Text GetReplacedFunctionName() { |
||||||
|
return replaced.Copy(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function string GetReplacedFunctionName_S() { |
||||||
|
return replaced.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function Text GetReplacerFunctionName() { |
||||||
|
return replacer.Copy(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function string GetReplacerFunctionName_S() { |
||||||
|
return replacer.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function SideEffect GetSideEffect() { |
||||||
|
effect.NewRef(); |
||||||
|
return effect; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class MockInitialClass extends AcediaObject; |
||||||
|
|
||||||
|
var public int counter; |
||||||
|
|
||||||
|
public final function int DoIt() { |
||||||
|
counter += 1; |
||||||
|
return counter; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class MockReplacerClass extends MockInitialClass; |
||||||
|
|
||||||
|
public final function int DoIt2() { |
||||||
|
counter += 2; |
||||||
|
return -counter; |
||||||
|
} |
||||||
|
|
||||||
|
public final function int DoIt3() { |
||||||
|
counter -= 1; |
||||||
|
return 7; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
/** |
||||||
|
* Set of tests for `Command` class. |
||||||
|
* Copyright 2021 - 2022 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class TEST_Unflect extends TestCase |
||||||
|
abstract; |
||||||
|
|
||||||
|
protected static function TESTS() { |
||||||
|
local MockInitialClass obj; |
||||||
|
|
||||||
|
obj = MockInitialClass(__().memory.Allocate(class'MockInitialClass')); |
||||||
|
Context("Replacing functions with `UnflectApi`"); |
||||||
|
Test_InitialReplacement(obj); |
||||||
|
Test_SecondReplacement(obj); |
||||||
|
Test_ReplacementWithSelf(obj); |
||||||
|
Test_RevertingReplacement(obj); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_InitialReplacement(MockInitialClass obj) { |
||||||
|
Issue("Functions aren't being replaced correctly the first time."); |
||||||
|
TEST_ExpectTrue(__().unflect.ReplaceFunction_S( |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"AcediaCore.MockReplacerClass.DoIt2", |
||||||
|
"testing")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == -2); |
||||||
|
TEST_ExpectTrue(obj.counter == 2); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == -4); |
||||||
|
TEST_ExpectTrue(obj.counter == 4); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_SecondReplacement(MockInitialClass obj) { |
||||||
|
Issue("Functions aren't being replaced correctly in case they were already replaced."); |
||||||
|
TEST_ExpectTrue(__().unflect.ReplaceFunction_S( |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"AcediaCore.MockReplacerClass.DoIt3", |
||||||
|
"testing")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 7); |
||||||
|
TEST_ExpectTrue(obj.counter == 3); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 7); |
||||||
|
TEST_ExpectTrue(obj.counter == 2); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_ReplacementWithSelf(MockInitialClass obj) { |
||||||
|
Issue("Attempting to replacing function with itself makes unexpected change."); |
||||||
|
TEST_ExpectFalse(__().unflect.ReplaceFunction_S( |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"testing")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 7); |
||||||
|
TEST_ExpectTrue(obj.counter == 1); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_RevertingReplacement(MockInitialClass obj) { |
||||||
|
Issue("Reverting replaced function doesn't work."); |
||||||
|
TEST_ExpectTrue(__().unflect.RevertFunction_S("AcediaCore.MockInitialClass.DoIt")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 2); |
||||||
|
TEST_ExpectTrue(obj.counter == 2); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 3); |
||||||
|
TEST_ExpectTrue(obj.counter == 3); |
||||||
|
|
||||||
|
Issue("Reverting already reverted function ends in success."); |
||||||
|
TEST_ExpectFalse(__().unflect.RevertFunction_S("AcediaCore.MockInitialClass.DoIt")); |
||||||
|
|
||||||
|
Issue("Reverting already reverted function leads to unexpected results."); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 4); |
||||||
|
TEST_ExpectTrue(obj.counter == 4); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
caseName = "Function replacement" |
||||||
|
caseGroup = "Unflect" |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class TypeCast extends Object; |
||||||
|
|
||||||
|
var Object nativeType; |
||||||
|
|
||||||
|
final function NativeCast(Object type) { |
||||||
|
nativeType = type; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UClass extends UState within Package; |
||||||
|
|
||||||
|
var int classFlags; |
||||||
|
var int classUnique; |
||||||
|
var Guid classGuid; |
||||||
|
var UClass classWithin; |
||||||
|
var name classConfigName; |
||||||
|
|
||||||
|
var array<struct RepRecord { |
||||||
|
var UProperty property; |
||||||
|
var int index; |
||||||
|
}> classReps; |
||||||
|
|
||||||
|
var array<UField> netFields; |
||||||
|
|
||||||
|
var array<struct Dependency { |
||||||
|
var UClass class; |
||||||
|
var int deep; |
||||||
|
var int scriptTextCRC; |
||||||
|
}> dependencies; |
||||||
|
|
||||||
|
var array<name> packageImports; |
||||||
|
var array<byte> defaults; |
||||||
|
var array<name> hideCategories; |
||||||
|
var array<name> dependentOn; |
||||||
|
|
||||||
|
var string defaultPropText; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UClassCast extends Object; |
||||||
|
|
||||||
|
var UClass nativeType; |
||||||
|
|
||||||
|
final function UClass Cast(Class type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UField extends Object |
||||||
|
abstract; |
||||||
|
|
||||||
|
var UField superField; |
||||||
|
var UField next; |
||||||
|
var UField hashNext; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UFieldCast extends Object; |
||||||
|
|
||||||
|
var UField nativeType; |
||||||
|
|
||||||
|
final function UField Cast(Field type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UFunction extends UStruct within UState |
||||||
|
dependson(Unflect); |
||||||
|
|
||||||
|
var byte functionMD5Digest[16]; |
||||||
|
|
||||||
|
var int functionFlags; |
||||||
|
var Unflect.Int16 nativeIndex; |
||||||
|
var Unflect.Int16 repOffset; |
||||||
|
var byte operPrecedence; |
||||||
|
|
||||||
|
var byte numParms; |
||||||
|
var Unflect.Int16 parmsSize; |
||||||
|
var Unflect.Int16 returnValueOffset; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UFunctionCast extends Object; |
||||||
|
|
||||||
|
var UFunction nativeType; |
||||||
|
|
||||||
|
final function UFunction Cast(Function type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UProperty extends UField within UField |
||||||
|
abstract; |
||||||
|
|
||||||
|
var int arrayDim; |
||||||
|
var int elementSize; |
||||||
|
var int propertyFlags; |
||||||
|
var name category; |
||||||
|
|
||||||
|
var byte repOffset[2]; |
||||||
|
var byte repIndex[2]; |
||||||
|
|
||||||
|
var transient int offset; |
||||||
|
var transient UProperty propertyLinkNext; |
||||||
|
var transient UProperty configLinkNext; |
||||||
|
var transient UProperty constructorLinkNext; |
||||||
|
var transient UProperty nextRef; |
||||||
|
var transient UProperty repOwner; |
||||||
|
|
||||||
|
var string commentString; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UPropertyCast extends Object; |
||||||
|
|
||||||
|
var UProperty nativeType; |
||||||
|
|
||||||
|
final function UProperty Cast(Property type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UState extends UStruct |
||||||
|
dependson(Unflect); |
||||||
|
|
||||||
|
var Unflect.Int64 probeMask; |
||||||
|
var Unflect.Int64 ignoreMask; |
||||||
|
var int stateFlags; |
||||||
|
var Unflect.Int16 labelTableOffset; |
||||||
|
var UField vfHash[256]; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UStateCast extends Object; |
||||||
|
|
||||||
|
var UState nativeType; |
||||||
|
|
||||||
|
final function UState Cast(State type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UStruct extends UField; |
||||||
|
|
||||||
|
var UTextBuffer scriptText; |
||||||
|
var UTextBuffer cppText; |
||||||
|
var UField children; |
||||||
|
var int propertiesSize; |
||||||
|
var name friendlyName; |
||||||
|
var array<byte> script; |
||||||
|
|
||||||
|
var int textPos; |
||||||
|
var int line; |
||||||
|
var struct EStructFlags { |
||||||
|
var bool native; |
||||||
|
var bool export; |
||||||
|
var bool long; |
||||||
|
var bool init; |
||||||
|
var bool unused1; |
||||||
|
var bool unused2; |
||||||
|
var bool unused3; |
||||||
|
var bool unused4; |
||||||
|
} StructFlags; |
||||||
|
|
||||||
|
var Property refLink; |
||||||
|
var Property propertyLink; |
||||||
|
var Property configLink; |
||||||
|
var Property constructorLink; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UStructCast extends Object; |
||||||
|
|
||||||
|
var UStruct nativeType; |
||||||
|
|
||||||
|
final function UStruct Cast(/*Core.Struct*/ Object type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UTextBuffer extends Object; |
||||||
|
|
||||||
|
var private native const pointer outputDeviceVtbl; |
||||||
|
|
||||||
|
var int pos, top; |
||||||
|
var string text; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class Unflect extends Object |
||||||
|
abstract; |
||||||
|
|
||||||
|
struct Int16 { |
||||||
|
var byte h, l; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Int64 { |
||||||
|
var int h, l; |
||||||
|
}; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,409 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2020 bibibi |
||||||
|
* 2020-2023 Shtoyan |
||||||
|
* 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UnflectApi extends AcediaObject; |
||||||
|
|
||||||
|
//! This API offers advanced reflection capabilities for Unreal Script. |
||||||
|
//! |
||||||
|
//! Currently, the API supports the ability to replace the code of existing functions with |
||||||
|
//! custom code. |
||||||
|
//! This can greatly simplify the process of bug fixing and hooking into game events. |
||||||
|
|
||||||
|
/// A variable responsible for replacing function code in real-time. |
||||||
|
/// This variable is used to support dynamic function replacement/patching and event interception |
||||||
|
/// at runtime. |
||||||
|
var private UFunctionCast functionCaster; |
||||||
|
|
||||||
|
/// Maps lower case function name (specifies by the full path "package.class.functionName") |
||||||
|
/// to a `FunctionRule` that completely describes how it was replaced |
||||||
|
var private HashTable completedReplacements; |
||||||
|
/// Maps lower case function name (specifies by the full path "package.class.functionName") |
||||||
|
/// to the `ByteArrayBox` with that function's original code. |
||||||
|
var private HashTable originalScriptCodes; |
||||||
|
|
||||||
|
var private LoggerAPI.Definition warnSameFunction; |
||||||
|
var private LoggerAPI.Definition warnOverridingReplacement, errFailedToFindFunction; |
||||||
|
var private LoggerAPI.Definition errReplacementWithoutSources, errCannotCreateReplacementRule; |
||||||
|
|
||||||
|
protected function Constructor() { |
||||||
|
functionCaster = new class'UFunctionCast'; |
||||||
|
completedReplacements = _.collections.EmptyHashTable(); |
||||||
|
originalScriptCodes = _.collections.EmptyHashTable(); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() { |
||||||
|
_.memory.Free(completedReplacements); |
||||||
|
_.memory.Free(originalScriptCodes); |
||||||
|
completedReplacements = none; |
||||||
|
originalScriptCodes = none; |
||||||
|
functionCaster = none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Reverts the replacement of the function's code, restoring its original behavior. |
||||||
|
/// |
||||||
|
/// The function to be reverted should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// It's worth noting that several function replacements do not stack. |
||||||
|
/// Even if [`ReplaceFunction()`] was called multiple times in a row to replace the same function, |
||||||
|
/// this method will cancel all the changes at once. |
||||||
|
/// |
||||||
|
/// This method returns true if the specified function was previously replaced and has now been |
||||||
|
/// successfully reverted. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// If the specified function cannot be found (but [`functionName`] isn't `none`), or if |
||||||
|
/// UnflectApi has not yet replaced it with any other function, this method will log an error. |
||||||
|
public final function bool RevertFunction(BaseText functionName) { |
||||||
|
local bool result; |
||||||
|
local FunctionReplacement storedReplacement; |
||||||
|
local ByteArrayBox storedSources; |
||||||
|
local Text functionNameLowerCase; |
||||||
|
local UFunction functionInstance; |
||||||
|
local SideEffect sideEffect; |
||||||
|
|
||||||
|
if (functionName == none) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
functionNameLowerCase = functionName.LowerCopy(); |
||||||
|
storedReplacement = FunctionReplacement(completedReplacements.GetItem(functionNameLowerCase)); |
||||||
|
if (storedReplacement != none) { |
||||||
|
storedSources = ByteArrayBox(originalScriptCodes.GetItem(functionNameLowerCase)); |
||||||
|
if (storedSources == none) { |
||||||
|
_.logger.Auto(errReplacementWithoutSources).Arg(functionNameLowerCase.Copy()); |
||||||
|
} else { |
||||||
|
functionInstance = FindFunction(functionNameLowerCase); |
||||||
|
if (functionInstance != none) { |
||||||
|
functionInstance.script = storedSources.Get(); |
||||||
|
completedReplacements.RemoveItem(functionNameLowerCase); |
||||||
|
sideEffect = storedReplacement.GetSideEffect(); |
||||||
|
_.sideEffects.RemoveInstance(sideEffect); |
||||||
|
result = true; |
||||||
|
} else { |
||||||
|
_.logger.Auto(errFailedToFindFunction).Arg(functionNameLowerCase.Copy()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
_.memory.Free4(storedReplacement, functionNameLowerCase, storedSources, sideEffect); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Reverts the replacement of the function's code, restoring its original behavior. |
||||||
|
/// |
||||||
|
/// The function to be reverted should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// It's worth noting that several function replacements do not stack. |
||||||
|
/// Even if [`ReplaceFunction()`] was called multiple times in a row to replace the same function, |
||||||
|
/// this method will cancel all the changes at once. |
||||||
|
/// |
||||||
|
/// This method returns true if the specified function was previously replaced and has now been |
||||||
|
/// successfully reverted. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// If the specified function cannot be found (but [`functionName`] isn't `none`), or if |
||||||
|
/// UnflectApi has not yet replaced it with any other function, this method will log an error. |
||||||
|
public final function bool RevertFunction_S(string functionName) { |
||||||
|
local bool result; |
||||||
|
local MutableText wrapper; |
||||||
|
|
||||||
|
wrapper = _.text.FromStringM(functionName); |
||||||
|
result = RevertFunction(wrapper); |
||||||
|
_.memory.Free(wrapper); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Determines whether the specified function has been replaced by UnflectApi. |
||||||
|
/// |
||||||
|
/// The function to be checked should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// If the function has been replaced, this method will return `true`; |
||||||
|
/// otherwise, it will return `false`. |
||||||
|
public final function bool IsFunctionReplaced(BaseText functionName) { |
||||||
|
local bool result; |
||||||
|
local Text functionNameLowerCase; |
||||||
|
|
||||||
|
if (functionName == none) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
functionNameLowerCase = functionName.LowerCopy(); |
||||||
|
result = completedReplacements.HasKey(functionNameLowerCase); |
||||||
|
_.memory.Free(functionNameLowerCase); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Determines whether the specified function has been replaced by UnflectApi. |
||||||
|
/// |
||||||
|
/// The function to be checked should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// If the function has been replaced, this method will return `true`; |
||||||
|
/// otherwise, it will return `false`. |
||||||
|
public final function bool IsFunctionReplaced_S(string functionName) { |
||||||
|
local bool result; |
||||||
|
local MutableText wrapper; |
||||||
|
|
||||||
|
wrapper = _.text.FromStringM(functionName); |
||||||
|
result = IsFunctionReplaced(wrapper); |
||||||
|
_.memory.Free(wrapper); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Replaces one function with another by modifying its script code in real-time. |
||||||
|
/// |
||||||
|
/// The reason for replacement must be specified and will serve as a human-readable explanation |
||||||
|
/// for a [SideEffect] associated with the replacement. |
||||||
|
/// |
||||||
|
/// If you need to replace a function in a class, follow these steps: |
||||||
|
/// |
||||||
|
/// 1. Create a new class that extends the class in which the function you want to replace is |
||||||
|
/// located. |
||||||
|
/// 2. Declare that function in the created class. |
||||||
|
/// 3. **DO NOT** change the function declaration and argument types/amount. |
||||||
|
/// 4. **DO NOT** create new local variables, as this can cause random crashes. |
||||||
|
/// If you need additional variables, make them global and access them using the |
||||||
|
/// `class'myNewClass'.default.myNewVariable` syntax. |
||||||
|
/// 5. If you want to call or override parent code, make sure to always specify the desired parent |
||||||
|
/// class name. |
||||||
|
/// For example, use `super(TargetClass).PostBeginPlay()` instead of `super.PostBeginPlay()`. |
||||||
|
/// This will prevent runaway loop crashes. |
||||||
|
/// 6. Make your edits to the function's code, and then call the replacement function: |
||||||
|
/// ```unrealscript |
||||||
|
/// _.unflect.ReplaceFunction( |
||||||
|
/// "package.class.targetFunction", |
||||||
|
/// "myNewPackage.myNewClass.newFunction"); |
||||||
|
/// ``` |
||||||
|
/// |
||||||
|
/// Following these steps will help ensure that your code changes are compatible with the rest of |
||||||
|
/// the codebase and do not cause unexpected crashes. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// This method can log error messages in cases where: |
||||||
|
/// |
||||||
|
/// * The specified function(s) cannot be found. |
||||||
|
/// * An attempt is made to replace a function with itself. |
||||||
|
/// * An attempt is made to replace a function that has already been replaced. |
||||||
|
public final function bool ReplaceFunction( |
||||||
|
BaseText oldFunction, |
||||||
|
BaseText newFunction, |
||||||
|
BaseText replacementReason |
||||||
|
) { |
||||||
|
local bool result; |
||||||
|
local Text oldFunctionLowerCase, newFunctionLowerCase; |
||||||
|
|
||||||
|
if (oldFunction == none) return false; |
||||||
|
if (newFunction == none) return false; |
||||||
|
|
||||||
|
oldFunctionLowerCase = oldFunction.LowerCopy(); |
||||||
|
newFunctionLowerCase = newFunction.LowerCopy(); |
||||||
|
result = _replaceFunction(oldFunctionLowerCase, newFunctionLowerCase); |
||||||
|
if (result) { |
||||||
|
RecordNewReplacement(oldFunctionLowerCase, newFunctionLowerCase, replacementReason); |
||||||
|
} |
||||||
|
_.memory.Free2(oldFunctionLowerCase, newFunctionLowerCase); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Replaces one function with another by modifying its script code in real-time. |
||||||
|
/// |
||||||
|
/// The reason for replacement must be specified and will serve as a human-readable explanation |
||||||
|
/// for a [SideEffect] associated with the replacement. |
||||||
|
/// |
||||||
|
/// If you need to replace a function in a class, follow these steps: |
||||||
|
/// |
||||||
|
/// 1. Create a new class that extends the class in which the function you want to replace is |
||||||
|
/// located. |
||||||
|
/// 2. Declare that function in the created class. |
||||||
|
/// 3. **DO NOT** change the function declaration and argument types/amount. |
||||||
|
/// 4. **DO NOT** create new local variables, as this can cause random crashes. |
||||||
|
/// If you need additional variables, make them global and access them using the |
||||||
|
/// `class'myNewClass'.default.myNewVariable` syntax. |
||||||
|
/// 5. If you want to call or override parent code, make sure to always specify the desired parent |
||||||
|
/// class name. |
||||||
|
/// For example, use `super(TargetClass).PostBeginPlay()` instead of `super.PostBeginPlay()`. |
||||||
|
/// This will prevent runaway loop crashes. |
||||||
|
/// 6. Make your edits to the function's code, and then call the replacement function: |
||||||
|
/// ```unrealscript |
||||||
|
/// _.unflect.ReplaceFunction( |
||||||
|
/// "package.class.targetFunction", |
||||||
|
/// "myNewPackage.myNewClass.newFunction"); |
||||||
|
/// ``` |
||||||
|
/// |
||||||
|
/// Following these steps will help ensure that your code changes are compatible with the rest of |
||||||
|
/// the codebase and do not cause unexpected crashes. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// This method can log error messages in cases where: |
||||||
|
/// |
||||||
|
/// * The specified function(s) cannot be found. |
||||||
|
/// * An attempt is made to replace a function with itself. |
||||||
|
/// * An attempt is made to replace a function that has already been replaced. |
||||||
|
public final function bool ReplaceFunction_S( |
||||||
|
string oldFunction, |
||||||
|
string newFunction, |
||||||
|
string replacementReason |
||||||
|
) { |
||||||
|
local Text oldWrapper, newWrapper, reasonWrapper; |
||||||
|
local bool result; |
||||||
|
|
||||||
|
oldWrapper = _.text.FromString(oldFunction); |
||||||
|
newWrapper = _.text.FromString(newFunction); |
||||||
|
reasonWrapper = _.text.FromString(replacementReason); |
||||||
|
result = ReplaceFunction(oldWrapper, newWrapper, reasonWrapper); |
||||||
|
_.memory.Free3(oldWrapper, newWrapper, reasonWrapper); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
// Does actual work for function replacement. |
||||||
|
// Arguments are assumed to be not `none` and in lower case. |
||||||
|
private final function bool _replaceFunction(Text oldFunctionLowerCase, Text newFunctionLowerCase) { |
||||||
|
local ByteArrayBox initialCode; |
||||||
|
local UFunction replace, with; |
||||||
|
|
||||||
|
replace = FindFunction(oldFunctionLowerCase); |
||||||
|
if (replace == none) { |
||||||
|
_.logger.Auto(errFailedToFindFunction).Arg(oldFunctionLowerCase.Copy()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
with = FindFunction(newFunctionLowerCase); |
||||||
|
if (with == none) { |
||||||
|
_.logger.Auto(errFailedToFindFunction).Arg(newFunctionLowerCase.Copy()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (replace == with) { |
||||||
|
_.logger.Auto(warnSameFunction).Arg(oldFunctionLowerCase.Copy()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
// Remember old code, if haven't done so yet. |
||||||
|
// Since we attempt it on each replacement, the first recorded `script` value will be |
||||||
|
// the initial code. |
||||||
|
if (!originalScriptCodes.HasKey(oldFunctionLowerCase)) { |
||||||
|
initialCode = _.box.ByteArray(replace.script); |
||||||
|
originalScriptCodes.SetItem(oldFunctionLowerCase, initialCode); |
||||||
|
_.memory.Free(initialCode); |
||||||
|
} |
||||||
|
replace.script = with.script; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Arguments assumed to be not `none` and in lower case. |
||||||
|
private final function UFunction FindFunction(Text functionNameLowerCase) { |
||||||
|
local string stringName; |
||||||
|
|
||||||
|
stringName = functionNameLowerCase.ToString(); |
||||||
|
// We need to make sure functions are loaded before performing the replacement. |
||||||
|
DynamicLoadObject(GetClassName(stringName), class'class', true); |
||||||
|
return functionCaster.Cast(function(FindObject(stringName, class'Function'))); |
||||||
|
} |
||||||
|
|
||||||
|
// Arguments are assumed to be not `none`. |
||||||
|
// `oldFunctionLowerCase` and `newFunctionLowerCase` are assumed to be in lower case. |
||||||
|
private final function RecordNewReplacement( |
||||||
|
Text oldFunctionLowerCase, |
||||||
|
Text newFunctionLowerCase, |
||||||
|
BaseText replacementReason |
||||||
|
) { |
||||||
|
local SideEffect oldSideEffect, newSideEffect; |
||||||
|
local FunctionReplacement oldRule, newRule; |
||||||
|
|
||||||
|
// Remove old `FunctionReplacement`, if there is any |
||||||
|
oldRule = FunctionReplacement(completedReplacements.GetItem(oldFunctionLowerCase)); |
||||||
|
if (oldRule != none) { |
||||||
|
_.logger |
||||||
|
.Auto(warnOverridingReplacement) |
||||||
|
.Arg(oldFunctionLowerCase.Copy()) |
||||||
|
.Arg(oldRule.GetReplacerFunctionName()) |
||||||
|
.Arg(newFunctionLowerCase.Copy()); |
||||||
|
oldSideEffect = oldRule.GetSideEffect(); |
||||||
|
_.sideEffects.RemoveInstance(oldSideEffect); |
||||||
|
_.memory.Free2(oldRule, oldSideEffect); |
||||||
|
} |
||||||
|
// Create replacement instance |
||||||
|
newSideEffect = MakeSideEffect(oldFunctionLowerCase, newFunctionLowerCase, replacementReason); |
||||||
|
newRule = class'FunctionReplacement'.static |
||||||
|
.Make(oldFunctionLowerCase, newFunctionLowerCase, newSideEffect); |
||||||
|
completedReplacements.SetItem(oldFunctionLowerCase, newRule); |
||||||
|
if (newRule == none) { |
||||||
|
_.logger |
||||||
|
.Auto(errCannotCreateReplacementRule) |
||||||
|
.Arg(oldFunctionLowerCase.Copy()) |
||||||
|
.Arg(newFunctionLowerCase.Copy()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Arguments are assumed to be not `none`. |
||||||
|
// `oldFunctionLowerCase` and `newFunctionLowerCase` are assumed to be in lower case. |
||||||
|
private final function SideEffect MakeSideEffect( |
||||||
|
Text oldFunctionLowerCase, |
||||||
|
Text newFunctionLowerCase, |
||||||
|
BaseText replacementReason |
||||||
|
) { |
||||||
|
local SideEffect sideEffect; |
||||||
|
local MutableText status; |
||||||
|
|
||||||
|
// Add side effect |
||||||
|
status = oldFunctionLowerCase.MutableCopy(); |
||||||
|
status.Append(P(" -> ")); |
||||||
|
status.Append(newFunctionLowerCase); |
||||||
|
sideEffect = _.sideEffects.Add( |
||||||
|
P("Changed function's code"), |
||||||
|
replacementReason, |
||||||
|
P("AcediaCore"), |
||||||
|
P("UnflectAPI"), |
||||||
|
status |
||||||
|
); |
||||||
|
_.memory.Free(status); |
||||||
|
return sideEffect; |
||||||
|
} |
||||||
|
|
||||||
|
// Get the "package + dot + class" string for DynamicLoadObject() |
||||||
|
private final static function string GetClassName(string input) { |
||||||
|
local array<string> parts; |
||||||
|
|
||||||
|
// create an array |
||||||
|
Split(input, ".", parts); |
||||||
|
|
||||||
|
// state functions |
||||||
|
if (parts.length < 3) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
if (parts.length == 4) { |
||||||
|
ReplaceText(input, "." $ parts[2], ""); |
||||||
|
ReplaceText(input, "." $ parts[3], ""); |
||||||
|
} else { |
||||||
|
ReplaceText(input, "." $ parts[2], ""); |
||||||
|
} |
||||||
|
return input; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
warnOverridingReplacement = (l=LOG_Error,m="Attempt to replace a function `%1` with function `%3` after it has already been replaced with `%2`.") |
||||||
|
warnSameFunction = (l=LOG_Error,m="Attempt to replace a function `%1` with itself.") |
||||||
|
errFailedToFindFunction = (l=LOG_Error,m="`UnflectApi` has failed to find function `%1`.") |
||||||
|
errReplacementWithoutSources = (l=LOG_Error,m="Cannot restore function `%1` - its initial source code wasn't preserved. This most likely means that it wasn't yet replaced.") |
||||||
|
errCannotCreateReplacementRule = (l=LOG_Error,m="`Cannot create new rule for replacing function `%1` with `%2` even though code was successfully replaces. This should happen, please report this.") |
||||||
|
} |
Loading…
Reference in new issue