anchor.diff:    StartupInfo: TStartupInfo;
     ProcessInfo: TProcessInformation;
   begin
  +  RT := rxUnits;
  +  if fSprites[RT] = nil then
  +  begin
  +    fSprites[RT] := TKMSpritePackEdit.Create(RT, fPalettes);
  +    RXName := ExeDir + 'data\Sprites\' + RXInfo[RT].FileName + '_a.rxx';
  +    fSprites[RT].LoadFromRXXFile(RXName);
  +  end;
  +
     StrList := TStringList.Create;
  -  RT := rxUnits;
  -  SpritePack := TKMSpritePackEdit.Create(RT, fPalettes);
  -  try
  -    RXName := ExeDir + 'data\Sprites\' + RXInfo[RT].FileName + '_a.rxx';
  -    SpritePack.LoadFromRXXFile(RXName);
   
  -    resUnits := TKMResUnits.Create;
  +  A := fResUnits[aUT].UnitAnim[aAction,aDir];
  +  for Step := 1 to A.Count do
  +  begin
  +    SpriteID := A.Step[Step]+1; //Sprites in units.dat are 0 indexed
  +    if SpriteID > 0 then
  +    begin
  +      fSprites[RT].ExportImageForInterp(fOrigSpritesDir + format('%.6d.png', [Step]), SpriteID);
  +    end;
  +  end;
  +  //Write out the first sprite again to create a loop
  +  SpriteID := A.Step[1]+1; //Sprites in units.dat are 0 indexed
  +  if SpriteID > 0 then
  +    fSprites[RT].ExportImageForInterp(fOrigSpritesDir + format('%.6d.png', [A.Count+1]), SpriteID);
   
  -    TempDir := ExeDir + 'SpriteInterp\';
  -    DainFolder := 'C:\Dev\kam_sprites\DAIN_APP Alpha 1.0\';
  -    DainExe := DainFolder+'DAINAPP.exe';
  -    DainParams := 'cmd.exe /C "'+DainExe+'" --cli 1 -o '+TempDir+' -p 0 -l 1 -in 8 -da 0 -a 1 -se 0 -si 1 -sr 0 -ha 0 --fast_mode 0 & pause';
  +  //Interpolate
  +  ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
  +  StartupInfo.cb := SizeOf(StartupInfo);
  +  CreateProcess(nil, PChar(fDainParams), nil, nil, false, 0, nil, PChar(fDainFolder), StartupInfo, ProcessInfo);
  +  WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  +  //ShellExecute(0, nil, 'cmd.exe', PChar(DainParams), PChar(DainFolder), SW_SHOWNORMAL);
   
  -    OrigSpritesDir := TempDir + 'original_frames\';
  -    InterpSpritesDir := TempDir + 'interpolated_frames\';
  -    ForceDirectories(OrigSpritesDir);
  +  //Import and reprocess
  +  Result := 8*A.Count;
  +  for Step := 1 to 8*A.Count do
  +  begin
  +    LoadFromPng(fInterpSpritesDir + format('%.15d.png', [Step]), pngWidth, pngHeight, pngData);
   
  -    //Export
  -    UT := utMilitia;
  -    Dir := dirE;
  -    A := resUnits[UT].UnitAnim[uaWalk,Dir];
  -    for Step := 1 to A.Count do
  +    //Determine Min/Max X/Y
  +    MinX := MaxInt;
  +    MinY := MaxInt;
  +    MaxX := -1;
  +    MaxY := -1;
  +    for Y := 0 to pngHeight-1 do
       begin
  -      SpriteID := A.Step[Step]+1; //Sprites in units.dat are 0 indexed
  -      if SpriteID > 0 then
  +      for X := 0 to pngWidth-1 do
         begin
  -        SpritePack.ExportImageForInterp(OrigSpritesDir + format('%.6d.png', [Step]), SpriteID);
  +        if pngData[Y*pngWidth + X] shr 24 <> 0 then
  +        begin
  +          MinX := Min(MinX, X);
  +          MinY := Min(MinY, Y);
  +          MaxX := Max(MaxX, X);
  +          MaxY := Max(MaxY, Y);
  +        end;
         end;
       end;
  -    //Write out the first sprite again to create a loop
  -    SpriteID := A.Step[1]+1; //Sprites in units.dat are 0 indexed
  -    if SpriteID > 0 then
  -      SpritePack.ExportImageForInterp(OrigSpritesDir + format('%.6d.png', [A.Count+1]), SpriteID);
  -
  -    //Interpolate
  -    ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
  -    StartupInfo.cb := SizeOf(StartupInfo);
  -    CreateProcess(nil, PChar(DainParams), nil, nil, false, 0, nil, PChar(DainFolder), StartupInfo, ProcessInfo);
  -    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  -    //ShellExecute(0, nil, 'cmd.exe', PChar(DainParams), PChar(DainFolder), SW_SHOWNORMAL);
  -
  -    //Import and reprocess
  -    for Step := 1 to 8*A.Count do
  +    //Crop
  +    if (MaxX > MinX) and (MaxY > MinY) then
       begin
  -      LoadFromPng(InterpSpritesDir + format('%.15d.png', [Step]), pngWidth, pngHeight, pngData);
  -
  -      //Determine Min/Max X/Y
  -      MinX := MaxInt;
  -      MinY := MaxInt;
  -      MaxX := -1;
  -      MaxY := -1;
  -      for Y := 0 to pngHeight-1 do
  +      newWidth := MaxX - MinX + 1;
  +      newHeight := MaxY - MinY + 1;
  +      SetLength(pngCrop, newWidth*newHeight);
  +      I := 0;
  +      for Y := MinY to MaxY do
         begin
  -        for X := 0 to pngWidth-1 do
  +        for X := MinX to MaxX do
           begin
  -          if pngData[Y*pngWidth + X] shr 24 <> 0 then
  -          begin
  -            MinX := Min(MinX, X);
  -            MinY := Min(MinY, Y);
  -            MaxX := Max(MaxX, X);
  -            MaxY := Max(MaxY, Y);
  -          end;
  +          pngCrop[I] := pngData[Y*pngWidth + X];
  +          Inc(I);
           end;
         end;
  -      //Crop
  -      if (MaxX > MinX) and (MaxY > MinY) then
  -      begin
  -        newWidth := MaxX - MinX + 1;
  -        newHeight := MaxY - MinY + 1;
  -        SetLength(pngCrop, newWidth*newHeight);
  -        I := 0;
  -        for Y := MinY to MaxY do
  -        begin
  -          for X := MinX to MaxX do
  -          begin
  -            pngCrop[I] := pngData[Y*pngWidth + X];
  -            Inc(I);
  -          end;
  -        end;
   
  -        //Save offsets .txt file
  -        StrList.Clear;
  -        StrList.Append(IntToStr(MinX - pngWidth div 2));
  -        StrList.Append(IntToStr(MinY - pngHeight div 2));
  -        //FIXME: Do SizeNoShadow properly
  -        StrList.Append(IntToStr(0));
  -        StrList.Append(IntToStr(0));
  -        StrList.Append(IntToStr(newWidth-1));
  -        StrList.Append(IntToStr(newHeight-1));
  +      //Save offsets .txt file
  +      StrList.Clear;
  +      StrList.Append(IntToStr(MinX - pngWidth div 2));
  +      StrList.Append(IntToStr(MinY - pngHeight div 2));
  +      //FIXME: Do SizeNoShadow properly
  +      StrList.Append(IntToStr(0));
  +      StrList.Append(IntToStr(0));
  +      StrList.Append(IntToStr(newWidth-1));
  +      StrList.Append(IntToStr(newHeight-1));
   
  -        ForceDirectories(ExeDir+'Sprites\3\');
  -        StrList.SaveToFile(ExeDir+'Sprites\3\'+format('3_93%.2d.txt', [Step]));
  -        SaveToPng(newWidth, newHeight, pngCrop, ExeDir+'Sprites\3\'+format('3_93%.2d.png', [Step]));
  -      end;
  +      ForceDirectories(ExeDir+'Sprites\3\');
  +      StrList.SaveToFile(ExeDir+'Sprites\3\'+format('3_%d.txt', [aPicOffset + Step]));
  +      SaveToPng(newWidth, newHeight, pngCrop, ExeDir+'Sprites\3\'+format('3_%d.png', [aPicOffset + Step]));
       end;
  +  end;
   
  -  finally
  -    SpritePack.Free;
  -    StrList.Free;
  -  end;
  +  FreeAndNil(StrList);
  +end;
  +
  +procedure TForm1.btnProcessUnitsClick(Sender: TObject);
  +var
  +  picOffset: Integer;
  +  dir: TKMDirection;
  +begin
  +  picOffset := 9300;
  +  for dir := dirN to dirNW do
  +    picOffset := picOffset + DoInterpUnit(utMilitia, uaWalk, dir, picOffset);
   end;
   
   procedure TForm1.FormCreate(Sender: TObject);
  
positive.code: 
    fPalettes := TKMResPalettes.Create;
    fPalettes.LoadPalettes(ExeDir + 'data\gfx\');
  end;
  
  end.
  
negative.code: uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
    KM_ResPalettes, KM_Defaults, KM_CommonTypes, KM_Points, KM_ResSprites, KM_ResSpritesEdit, KM_Pics, KM_ResUnits;
  
  type
    TForm1 = class(TForm)
  