Мутаторы и UnrealScript

Декомпиляция UnrealScript в Killing Floor: чистка вывода UE Explorer

Уважайте авторов модов и указывайте их. Это не руководство по воровству кода.

Некоторые авторы вырезают из пакетов исходный текст, и сообщество не может ни доработать мод, ни поучиться на нём. У каждого автора свои причины; многолетний спор об этом пересказан в статье Защита кода: почему вырезание исходников ничего не защищает. Здесь мы не спорим ни в какую сторону, а показываем, как вернуть читаемый код с помощью UE Explorer от Eliot (Unreal-Library) и привести в порядок то, что он выдаёт.

Дерево классов и свойства в KF Editor Дерево классов и свойства актора в KF Editor.

Байт-код против UnrealScript

Компиляция мутатора превращает UnrealScript в байт-код движка. Процесс необратим: многие конструкции, например цепочки if/else и циклы, не имеют прямого представления в байт-коде. Декомпилятор не может надёжно их восстановить и печатает более запутанный, хотя функционально тот же код. Ниже типичные превращения и как их откатить.

Циклы for

UE Explorer не восстанавливает циклы for. Такой исходник:

// modify zeds in special squads
function ModifySquadArray(out array<SpecialSquad> SquadArray)
{
  local int i, j;

  for (i = 0; i < SquadArray.Length; i++)
  {
    for (j = 0; j < SquadArray[i].ZedClass.Length; j++)
    {
      ReplaceMonsterClass(SquadArray[i].ZedClass[j]);
    }
  }
}

возвращается метками и goto:

function ModifySquadArray(out array<SpecialSquad> SquadArray)
{
  local int i, j;

  i = 0;
  J0x07:
  // End:0x69 [Loop If]
  if(i < SquadArray.Length)
  {
    j = 0;
    J0x1E:
    // End:0x5F [Loop If]
    if(j < SquadArray[i].ZedClass.Length)
    {
      ReplaceMonsterClass(SquadArray[i].ZedClass[j]);
      ++ j;
      // [Loop Continue]
      goto J0x1E;
    }
    ++ i;
    // [Loop Continue]
    goto J0x07;
  }
  //return;
}

Это компилируется, но большие функции становятся нечитаемыми. Уберите метки и превратите каждую пару [Loop If] + goto обратно в for: оператор перед меткой — инициализация, if — условие, ++ перед goto — шаг.

// [Explicit Break] замените на break;, а // [Explicit Continue] на continue;.

Блоки replication

Экспортируется всё правильно, кроме того, что у условий репликации пропадают точки с запятой:

replication
{
  // Pos:0x000
  reliable if(bNetInitial && Role == ROLE_Authority)
    CsHDRI
}

Верните их вручную:

replication
{
  reliable if (bNetInitial && Role == ROLE_Authority)
    CsHDRI;
}

defaultproperties

UE Explorer их показывает, но правильность не гарантирована: у экспортёра есть известные ошибки, а подобъекты в defaultproperties восстанавливаются плохо. Экспортируйте пакет штатным ucc и перенесите его defaultproperties поверх вывода UE Explorer:

ucc.exe batchexport PACKAGENAME.u class uc ../DIRECTORY/
pause

Запускать из папки System. batchexport пишет по .uc на каждый класс с правильными значениями по умолчанию, даже если тела функций вырезаны.

Цепочки else if

UE Explorer печатает только обычные if и вкладывает каждый else if:

static function AddDefaultInventory(KFPlayerReplicationInfo KFPRI, Pawn P)
{
  // End:0x44
  if(KFPRI.ClientVeteranSkillLevel == 5)
  {
    KFHumanPawn(P).CreateInventoryVeterancy("CsHDMut.W_Bullpup", default.StartingWeaponSellPriceLevel5);
  }
  // End:0x8E
  else
  {
    // End:0x8E
    if(KFPRI.ClientVeteranSkillLevel == 6)
    {
      KFHumanPawn(P).CreateInventoryVeterancy("CsHDMut.W_AK47AssaultRifle", default.StartingWeaponSellPriceLevel6);
    }
  }
  //return;
}

Разверните:

static function AddDefaultInventory(KFPlayerReplicationInfo KFPRI, Pawn P)
{
  if (KFPRI.ClientVeteranSkillLevel == 5)
  {
    KFHumanPawn(P).CreateInventoryVeterancy("CsHDMut.W_Bullpup", default.StartingWeaponSellPriceLevel5);
  }
  else if (KFPRI.ClientVeteranSkillLevel == 6)
  {
    KFHumanPawn(P).CreateInventoryVeterancy("CsHDMut.W_AK47AssaultRifle", default.StartingWeaponSellPriceLevel6);
  }
}

Значения enum в виде чисел

UE Explorer может печатать значения перечислений как числа. Найдите определение enum и верните имена. Элементы нумеруются с 0:

enum ENetMode
{
  NM_Standalone,        // 0
  NM_DedicatedServer,   // 1
  NM_ListenServer,      // 2
  NM_Client             // 3
};
ДекомпилированоЧитаемоГде определено
Level.NetMode == 3Level.NetMode == NM_ClientEngine/Classes/LevelInfo.uc
RemoteRole = 2RemoteRole = ROLE_SimulatedProxyEngine/Classes/Actor.uc

В исходниках скриптов KF на GitHub (InsultingPros/KillingFloor) есть все стандартные классы. Если открыть их в VS Code, «перейти к определению» найдёт любой enum за секунды. В гайде по UT Package Tool есть готовые таблицы самых частых enum.

Чек-лист чистки

  1. Экспортировать defaultproperties через ucc batchexport и перенести в декомпилированные классы.
  2. Собрать циклы for из меток и goto, превратить комментарии explicit break/continue в операторы.
  3. Добавить недостающие точки с запятой в replication.
  4. Развернуть вложенные else { if } в else if.
  5. Заменить числа enum на имена.
  6. Скомпилировать через ucc make и проверить, прежде чем что-то публиковать.
  7. Указать автора оригинала.