Tłumaczenie cnc-ddraw z C na Delphi i uniwersalizacja tej dllki

0

Witam. Zwykle chętnie pomagam w problemach związanych z Delphi i Pascalem, jeśli tylko potrafię. Jednak nie ukrywam, że z języków C jestem totalna lama i moja wiedza jest bardzo mała. Początkowo prosiłem na PW o pomoc Azariena, który kiedyś doradzał mi przy tłumaczeniu plików SDK dla pluginów do odtwarzacza XMPlay, jednak Azarien do tej pory mi nie odpisał. Jeżeli to teraz czytasz i znajdziesz czas to proszę odpisz :) Anyway, to zaadaprowanie kodu z C było moim pierwszym większym tłumaczeniem z C na Delphi i jakoś sobie poradziłem tworząc plugin do obsługi klawiszy multimedialnym w XMPlay'u. Jednak teraz szukam osób chętnych do pomocy przy przetłumaczeniu pewnego źródla z C na Delphi, a także "uniwersalizacji" tego kodu tak aby mógł działać możłiwie z każdą starszą grą. Chodzi o projekt ze strony: http://hifi.iki.fi/cnc-ddraw dla C&C Redalert sprawdza się dobrze. Jednak ja chciałbym aby można było uruchomić stare gry bez skopanych kolorów także pod Windows 7, bez konieczności "ubijania" procesu explorer.exe. Póki co przygotowałem sobie to co jest dołaczone do tego posta. W katalogu głownym ! znajduje się krótki kod w Delphi, po jego skompilowaniu otrzymujemy ddraw.dll, która póki co tylko po wrzuceniu do katalogu z grą "Wacki - Kosmiczna Rozgrywka", powoduje że mamy ok kolory pod Windows 7, jednak zadziała to dopiero za drugim uruchomieniem, a później nie działa, bo nie umiem zwolnić dllki, gdyż ddraw.dll nie ma chyba wywoływanego DLL_PROCESS_DETACH przez tę gre i nie mogę zwolnić używanej oryginalnej ddraw.dll. A próbowałem na rózne sposoby. Nie można wywołąć FreeLibrary w trakcie pracy gry bo się ona wysypie. Niestety w innych grach do których chciałem zastosować ten "myk" z wrzuceniem ddraw.dll, ale ani na "Księcia i Tchórza" ani na "Die by The Sword" to nie podziałalo. Wrzucenie oryginalnej dllki ze strony pod adresem powyżej do tych gier powoduje że się one wysypują. Dlatego chciałem mając przetłumaczony kod ddraw.dll z C na Delphi, spróbowac zaadoptować ją do innych gier. Nie pisałem póki co w tej sprawie do autora dllki dla RedAlerta, bo On pewnie i tak nie pisze w Delphi i nie wiem czy by pomógł. A z takiej działającej i uniwersalnej dllki ucieszyli by się pewnie fani wielu starych gier w tym StarCrafta, bo póki co sposobem jest albo odpalenie na wirtualnej maszynie ze starym systemem albo odpowiedni wpis w rejestrze. Niestety przy wpisie albo kombinacji tak jak dla Wacków, po przełaczeniu Alt+Tab na Pulpit i powrocie do okna gry kolory znowu są skopane. Pomaga tylko ubicie na czas grania expolorer.exe. Napisałem sobie do tego celu działające loadery, które przywrócają proces explorer.exe po wyjściu z gry, ale to metoda mało elegancka i jak wnioskuje z tej modyfikacji "C&C Red Alert" są inne skuteczne sposoby. Bo okno Red Alerta możemy przełaczać i kolory się nie psują. Za pewne dlatego, że dla celów rysowania autor tego ddraw.dll stwórzył włąsne okno i przechwytuje komunikaty do niego skierowane w odpowiedni sposób oraz po tym oknie chyba rysuje. Chodzi mi najpierw o przetłumaczenie kodu który jest w podkatalogu DDRAW_ORIGINAL_SOURCE na Delphi, tak aby skompilował się pod Delphi 7. Pomijam moduł screenshot.c bo nie znam zgrabnej biblioteki dla Delphi do plików png, która działała by w WinAPI. A ja chce jak najmniejszą możłiwie dllke, a nie spasła która się nie skompiluje z modułami z http://kolmck.net - zresztą ScreenShoty mozna sobie darować, po co to komu jak w razie czego jest Fraps :) A wspomniane "Wacki" w wersji "poprawionej" przeze mnie i działającej bez CD/ISO można znaleźć na trzy literowym portalu z "róznościami" ;) A teraz prosba do chętnych, bo ja mam mało czasu ze względu na dużą ilośc godzin spędzanych w pracy poza tym słaba znajomośc C. Otóż przygotowałem potrzebne moduły dla OpenGL'a i DirectXa oraz zaczątek źródła w podkatalogu `DDRAW_TRANSLATED_TO_DELPHI. I tutaj pare pytań jeżeli przejrzyćie kod w C:

  1. Czy dało by się to przetłumaczyć mając te moduły co dołaczyłęm dla DirectX'a i OpenGL'a? Pomijam StrStr, dołaczanie stdio i ewentualnie zamiane printfów na Delphiową funkcję taką jak w podkatalogu TEMPORARY_01.
  2. Czy StrStr to na pewno to samo co Pos tylko z odwróconymi parametrami?
  3. Czy LPVoid w tym kodzie to na pewno Pointer?
  4. Czy taki zapis (fragment):
    if(lpDDDriverCaps)
    {
        lpDDDriverCaps->dwSize = sizeof(DDCAPS);
        lpDDDriverCaps->dwCaps = DDCAPS_BLT|DDCAPS_PALETTE;
// ...

to to samo co if lpDDDriverCaps <> nil then // ...?
5. Czy wszelkie te structy w kodzie C można śmiało zamieniać na packed record?
6. Czy takie zapisy jak:

struct IDirectDrawImplVtbl
{
    HRESULT(__stdcall *QueryInterface) (IDirectDrawImpl *, const IID* const riid, LPVOID * ppvObj);
    ULONG(__stdcall *AddRef) (IDirectDrawImpl *);
    ULONG(__stdcall *Release) (IDirectDrawImpl *);

Mozna smiało przetłumaczyć na wzór:

type
  TJakasFunkcja = function(Parametr : string) : boolean;
  Cosik = packed record
    JakasFuncja : ^TJakasFunkcja;
  end;

Pomijam zawartość funkcji - chodzi mi tylko o ideę. I sorry za wstawianie kodu nie Delphiowego w znaczniki code, ale nie wiem czy w cpp było by ok. Wybaczcie też rozpisanie się. Wszelkie uwagi na temat kodu i odpowiedzi na moje lamerskie pytanie mile widzane. Jakby właśnie przede wszystkim ktoś fachowym okiem rzucił by na oryginalny kod C i zaopiniował czy w ogóle branie się za przerabianie kodu tak aby działał w innych grach się powiedzie? W RedAlercie działa elegancko, bo można przełaczać okno Alt+Tab i nie kopią się kolory. Problem jest tylko ze scrollem myszką. Nowsza wersja ma to poprawione, ale za to filmiki się tną. Mi już nie chodzi o ddraw.ini z ustawieniami, niech gra sobie działa nawet w oryginalnej 640x480, a te całe "hackowanie" coś tam z kursorem myszki też mi nie potrzebne. Chodzi tylko aby paleta 8 biotwa wyswietlała się ok, czyli tak jak jeszcze w Windowsach XP i była zachowana po Alt+Tab, ale bez kombinacji z ubijaniem explorer.exe. No pora iść spać, bo dzisiaj do roboty na nockę. Czekam na odpowiedzi. Wybaczcie jeszcze raz rozpisanie się i ewentualne literówki, ale późna pora oraz pośpiech i to pewnie dlatego :)

0
  1. tak, wszystko można przetłumaczyć z C na Delphi. tylko to jest dużo kodu, czy na pewno warto?
  2. Pos zwraca indeks, strstr zwraca wskaźnik na znaleziony podciąg. poza tym strstr operuje na char*, czyli delphiowym pchar. W Delphi jest StrPos działające tak jak strstr.
  3. tak
  4. tak
  5. hmm, niekoniecznie packed. ja bym zaczynał od zwykłego record i sprawdzał czy sizeof() wychodzi taki sam.
  6. można, ale to mi wygląda na pokrętną implementację interfejsu w C, w Delphi jest na to obiektowy typ interface.

co do meritum: nie wiedziałem o problemach z paletą w Windows 7. próbowałeś odpalać gry w trybie zgodności, z ustawioną opcją wyłączenia kompozycji i na 256 kolorów?

0

Gratuluję odwagi.

Zadanie niełatwe, bo jest wiele różnic między tymi językami.

Proponuję zacząć od gotowych narzędzi do konwersji C->Pascal, i jeśli coś nie działa, zastanowić się czy to jest potrzebne.
http://stackoverflow.com/questions/100596/best-resources-for-converting-c-c-dll-headers-to-delphi

0

@Azarien: dziękuję za odpowiedzi, co do pytania szóstego, to szczerze pisząc nigdy nie musiałem używać interface w swoich kodach, więc mogę spróbować zrobić w taki sposób jak napisałem przykładowo i powinno zadziałać?

@vpiotr: dzięki za gratulację. No chce mimo wszystko spróbować, w przerwach między kolejnymi zmianami w pracy. Co do tych gotowych narzędzi na tej stronie, męczę się z rejestracją na embarcadero.com żebu zobaczyć ten CToDelphi, bo albo już nie mam tam konta albo dane zapomniałem, mimo że mój e-mail jest w bazie, a hasła coś nie moge wysłać nowego na e-mail przez klikane w przycisk na stronie. Natomiast co do narzędzia z http://rvelthuis.de/programs/convertpack.html to niestety mam Delphi 7, więc nie użyję go, a nie chce kołować pirata i go instalowac, bo wystarcza mi Delphi 7 Personal. Chyba, że Ty lub Azarien albo ktoś inny ma odpowiednie Delphi i móglby mnie wspomóc konwertując pliki z podkatalogu DDRAW_ORIGINAL_SOURCE tym conertpackiem. A i oryginalnego kodu nie jest aż tak dużo, więc jakbym lepiej znał C/C++ to bym już dawno sporo przetłumaczył samodzielnie, a tak to będzie dla mnie męka, ale zawsze można spróbować i przy okazji się czegoś nauczyć, a później łatiej będzie mi analizować kod i spróbować go przerobić dla innych gier niż Red Alert.

EDIT: CToPas 2.0 działa tak sobie, coś tam pomodził, ale nie jest za idealnie, trzeba i tak poprawiać kod.
A i mam jeszcze pytanie co to oznacza to słowo "FAR" i jak je uwzględnić w Delphi, czy można je zignorować? Na przykład w takim fragmencie:

HRESULT __stdcall ddraw_clipper_GetHWnd(IDirectDrawClipperImpl *This, HWND FAR *a)
{
    printf("IDirectDrawClipper::GetHWnd(This=%p, ...)\n", This);
    return DD_OK;
}

A i mam jeszcze taki problem
Zacząłem tłumaczenie od clipper.c i clipper.h i mam na przykład

   IDirectDrawImplVtbl = packed record
{
    HRESULT(__stdcall *QueryInterface) (IDirectDrawImpl *, const IID* const riid, LPVOID * ppvObj);
    ULONG(__stdcall *AddRef) (IDirectDrawImpl *);
    ULONG(__stdcall *Release) (IDirectDrawImpl *);

Ale jak widać funkcje wymagają typu IDirectDrawImpl, ten zaś wymaga typu IDirectDrawImplVtbl i mam zagwostkę, bo chyba wcześniej nie miałem takiej sytuacji, więc nie wiem jak w Delphi zrobić, aby te typy się "widziały" i wszystko było ok? Ponieważ nie mogę dodać na przemienne do sekcji uses te moduły bo będzie błąd o circular reference.

typedef struct IDirectDrawImpl
{
    struct IDirectDrawImplVtbl *lpVtbl;

    ULONG Ref;

Jeszcze jedno pytanie, bo nie mogę się doszukać jak przekonwerotwać REFIID to jest to samo co TGUID?

0

FAR nic nie znaczy. olej.

a co do „circular reference” to powinno się kompilować coś takiego:

type PIDirectDrawImplVtbl = ^IDirectDrawImplVtbl;
     PIDirectDrawImpl = ^IDirectDrawImpl;
     
     IDirectDrawImplVtbl = packed record
        function QueryInterface(dd:PIDirectDrawImpl; riid:PIID; ppvObj:pointer):HRESULT; stdcall;
        ...
     end;

     IDirectDrawImpl = record
        lpVtbl:PIDirectDrawImplVtbl;
        Ref:ULONG;
        ...
     end;

(ważne żeby wszystko było w obrębie tego samego bloku type).

0

@vpiotr, @Azarien: dziękuję Wam bardzo za odpowiedzi. Z linkami od Piotra zapoznam się później, a porady i informacje Azariena też bardzo mi się oczywiście przydały, bo coś w ten sposób kombinowałem, ale widocznie coś źle robiłem. I dzięki tym poradą mogę ruszyć dalej. A jednak nie będzie właśnie wcale lekko to przetłumaczyć, ale nie zrażam się, jak będę miał tylko trochę wolnego czasu, to postaram się sukcesywnie to przetłumaczyć.

EDIT: mam jeszcze takie pytanie, bo nie musiałem tego ogarniać wcześniej. Jak taki kod móc zadeklarować w sekcji interface modułu i wywołać z innego modułu. Chodzi mi o to, że chcę zastąpić printf z C aby wszelkie \n podmieniał na #13#10, przy użyciu StringReplace i chciałem to dopisać w kodzie funkcji. A załadować dynamicznie funkcję printf nie umiem, bo ona ma nietypowe parametry w postaci listy.

function _FormatC(const Format : string) : string; cdecl;
const
  StackSlotSize = SizeOf(Pointer);
var
  Args : VA_List;
  Buffer : array[0..1024] of Char;
begin
  Args := VA_List(PAnsiChar(@Format) + ((SizeOf(Format) + StackSlotSize - 1) and not (StackSlotSize - 1)));
  SetString(Result, Buffer, wvsprintfA(Buffer, PChar(Format), Args));
end;

var
  FormatC : function(const Format : string) : string; cdecl varargs = _FormatC;

I jeszcze takie pytanie bo wolę się upewnić, czy taki kod w C:

HRESULT __stdcall ddraw_QueryInterface(IDirectDrawImpl *This, REFIID riid, void **obj)
{
    printf("DirectDraw::QueryInterface(This=%p, riid=%08X, obj=%p)\n", This, (unsigned int)riid, obj);

    *obj = This;

    return S_OK;
}

Można na Delphi przetłumaczyć tak jak poniżej czy jakoś inaczej? Dodam, że REEFIID zadeklarowałem jako DWORD, bo tak wygooglowałem we fragmencie jakiegoś kodu. Natomiast poniżej PPointer = ^Pointer.

function ddraw_QueryInterface(This : PIDirectDrawImpl; riid : REFIID; obj : PPointer) : HRESULT; stdcall;
begin
  printf('DirectDraw::QueryInterface(This=%p, riid=%08X, obj=%p)\n', This, riid, obj);
  Obj^ := This;
  Result := S_OK;
end;

A i w ogóle teraz kombinuje tak żeby się kompilowało, ale może Azarien albo ktoś inny potwierdzi czy dobrze kodzę. Bo w Delphi wiadomo recordów nie idzie wypełnić, tak jak w C++ że podaje się tylko czym wypełniamy bez tego Func_01_01, dla kolejnej dam Func_01_02 i tak dalej, a dla kolejnego rekordu 02_01, 02_02 i tak dalej. Oto wycinek kodu:

type
  TFunc_01_01 = function(This : PIDirectDrawImpl; riid : REFIID; obj : PPointer) : HRESULT; stdcall;

  IDirectDrawImplVtbl = packed record
      Func_01_01 : ^TFunc_01_01;
      //...
  // I dla testów poniżej deklaracji:
var
  a : IDirectDrawImplVtbl;

implementation

function ddraw_QueryInterface(This : PIDirectDrawImpl; riid : REFIID; obj : PPointer) : HRESULT; stdcall;
begin
  printf('DirectDraw::QueryInterface(This=%p, riid=%08X, obj=%p)\n', This, riid, obj);
  Obj^ := This;
  Result := S_OK;
end;

begin
  a.Func_01_01 := @ddraw_QueryInterface;
end.

1 użytkowników online, w tym zalogowanych: 0, gości: 1