Mode X und EGA Parrallax

  • Hach, C++ vor ISO C++98 ist einfach eine Pein. :D Gutes altes fopen und fread wäre vermutlich leichter...


    Man müsste mal nachschauen, was die Borland Implementierung von istream so anbietet.

    root42 auf YouTube


    80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, GUSar Lite & TNDY & SnarkBarker, PC MIDI Card + SC55 + MT-32, XT CF Lite, OSSC 1.6

  • fopen / fread

    Wenn ihr da ein schnelles gutes Beispiel für hättet würde ich das gerne verwenden, dann muss ich nicht so oft umbauen. und testen.

    Aus dem Kopf:

    Also mit fopen öffnen, "rb" steht für "read binary", dann solange nicht ende im gelände mit fread einlesen (Zeiger auf Puffer, Größe der Elemente in Bytes, Anzahl Elemente, dann pointer auf File). Der fread Call liefert die Zahl der tatsächlich gelesenen Elemente(!) zurück. Am Ende dann die Datei mit fclose schließen.

    root42 auf YouTube


    80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, GUSar Lite & TNDY & SnarkBarker, PC MIDI Card + SC55 + MT-32, XT CF Lite, OSSC 1.6

  • Noch eine Alternative wäre evtl. klassisches io, conio, fcntl:

    Code
    handle = open(name, O_RDONLY | O_BINARY);
    if (handle < 0) steige mit fehlermeldung aus
    read(handle, sprite[...][...], 1026);

    Turbo C / C++ hatte als Default Textdateien, darum, mode "rb" für read-only binary, also nicht CR/LF manipulieren.

    Code
    FILE * fp = fopen(const char * filename, const char * mode);
    fread(void * buffer, size_t element size, size_t number of elements, FILE * fp);

    Mit einem zusätzlichen Cast wäre das dann ungefähr, wobei sizeof unsigned char einfach 1 ist:

    Code
    fp = fopen("datei.tex", "rb");
    for offset2 ...
      gelesen = fread((void *)&sprites[offset2][0], sizeof(unsigned char), 1026, fp);
      if gelesen != 1026 dann fehlermeldung

    Diese Variante müsste gleichwertig sein, könnte aber bei altem Compiler schneller sein, ein langes Element statt 1026 einzelne Bytes:

    Code
    fp = fopen("datei.tex", "rb");
    for offset2 ...  gelesen = fread((void *)&sprites[offset2][0], 1026 * sizeof(unsigned char), 1, fp);
      if gelesen != 1 dann fehlermeldung


    Es gab wohl Compiler, die bei fread tatsächlich viele einzelne fgetc für einzelne Bytes gemacht haben oder eine Schleife über die Elemente. Vernünftige Compiler sollten aber einfach element size * number of elements ausrechnen und dann alle gewünschten Daten auf einmal als einen Block lesen, egal ob es 1026 * 1 oder 1 * 1026 sind.


    Im Prinzip könntest du auch ein eindimensionales sprites und dann sprites[offset2*1026 + offset1] nehmen, macht die Sache aber nicht unbedingt schöner.


    Fun fact: Eigentlich müsste deine ursprüngliche Variante auch ohne das Wort "read" möglich sein?

    Code
    for offset2 ...
      for offset1 ...
        ifs>>sprites[offset2][offset1];

    aber das wäre dann wieder nicht optimiert, weil es alle Bytes einzeln liest. Eventuell kann dein iostream read oder fstream read nur einzelne Elemente und keine Blöcke, was sagt deine Headerdatei? Andererseits wären bei neueren Compilern alle Skalare, entsprechend vorbereitete Klassen und Arrays davon davon "trivially copyable".

  • ...funktioniert leider alles nicht korrekt bei dem kompiler, es produziert genau die selben fehler.

    Manchmal kann es nur 201 bytes lesen manchmal meckert er 0 bytes dabei sind die Dateien sogar mit 200 texturen gfüttert und haben alle 200kb.

    Werde ich wohl weiterhin alles byte für byte einlesen.

  • Theoretisch könnte es dir passieren, dass ein read() Aufruf weniger Bytes liefert, als du wolltest, auch wenn das in DOS selten ist, also könnte man das "ifs.read(..., 1026)" narrensicherer ersetzen durch:


    Beim von dir beschriebenen Problem hilft der von mir erwähnte Stil, wie man damit umgehen kann, wenn zu wenig Bytes gelesen werden :) Wenn deine 50 Sprites zu je 1026 Bytes in deiner Datenmatrix am Ende ein zusammenhängender Block sind, würde ich statt 50 mal 1026 Bytes gleich 51300 Bytes am Stück lesen, aber dafür dann auf die "fehlertolerante" Art mit offset1 und remaining. Weniger Bytes als angefordert zu lesen ist bei read bzw. fread auch gar kein Fehler, das kann offiziell jederzeit passieren. Also gehört es eh zum guten Ton, damit zu rechnen :)

  • retval = ifs.read(sprite[offset2...][offset1], remaining)


    die Zeile funktioniert so ja leider nicht, mann kann nur offset2 eintragen.


    (remaining=1026) das gibt quasie vor das er in offset1 die ersten 1026 bytes füllt, wenn er welche vergisst, kann ich nicht sagen mach bei offset 1 bitte ab dem X. Wert weiter.

  • Der Gedanke ist tatsächlich, dass immer ab dort, wo read oder fread aufgehört hat, weitergelesen wird, bis alle Daten gelesen sind. Dafür musst du in der Lage sein, die Stelle in der Variable anzugeben. Notfalls eben mit Hilfe verschiedener Casts.


    Wenn dein Compiler für read (aber nimm lieber das klassische fread, die Grundidee bleibt gleich!) echt keine 2-dimensionalen Ziele erlaubt, kannst du den Trick mit dem Cast und der &Adresse verwenden, oder notfalls einen Pointer auf einen 1-dimensionalen Array auf die gleiche Adresse wie die von MXsprite[0][0] zeigen lassen und dann eben den 1-dimensionalen Offset von Hand ausrechnen. Allerdings hat der dann 32 bit, weil es ja ein huge Memory Model Ding mit mehr als 64 kB ist.


    Da jede einzelne Texturdatei kleiner als 64 kB ist, müsstest du trotzdem effizient fread benutzen können, wenn der Compiler intelligent genug ist, zu merken, dass für den fread Aufruf keine besondere Huge-Magie nötig ist, sondern Far-Pointer ausreichen: https://de.wikipedia.org/wiki/Turbo_C#Speichermodelle


    Der Witz bei Huge-Variablen ist, dass trotz 16-bit Compiler mehr als 64 kB in eine Variable passen. Dafür zahlst du aber einen hohen Rechenzeit-Preis, weil jeder Zugriff intern 32-bit Offsets in 16-bit Segment und 16-bit Offset umrechnen muss. Darum willst du möglichst wenige Zugriffe haben. Alternativ könntest du dir eine Sammlung von mehreren Far-Variablen machen, die jeweils nur Platz für eine Texturdatei haben. Da hat dann jede Textur-Variable ein eigenes, konstantes Segment und innerhalb der Variable wird dann effizient mit 16-bit Offsets gearbeitet.

  • ...funktioniert leider alles nicht korrekt bei dem kompiler, es produziert genau die selben fehler.

    Manchmal kann es nur 201 bytes lesen manchmal meckert er 0 bytes dabei sind die Dateien sogar mit 200 texturen gfüttert und haben alle 200kb.

    Werde ich wohl weiterhin alles byte für byte einlesen.

    Die Variante mit fopen / fread funktioniert nicht? Kann ja aber nicht Sinn der Sache sein, dass du einfach die lahmste Version nimmst, nur weil wir den Fehler nicht gefunden haben. :) Da gibt's ja gewiß eine logische Erklärung für.

    root42 auf YouTube


    80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, GUSar Lite & TNDY & SnarkBarker, PC MIDI Card + SC55 + MT-32, XT CF Lite, OSSC 1.6

  • Fopen fread funktioniert nicht so ganz.


    Sprites[offset2][offset1] offset1 kann man da leider nicht festlege, das zieht sich c ja selber wenn ich angebe 1026 bytes dann werden im offset1 die bytes 0-1026 befüllt.

    Wenn jetzt zu wenig eingelesen wird kann ich leider nicht einfach festlegen offset1 ab wert XY sollen gefüllt werden.


    Ich verstehe nicht so ganz warum immer an den selben stellen zu wenig Bytes eingelesen werden immer die selben Texturen scheinen gestückelt zu sein. Kann es sein das fread bei bestimmten Zeichen bytes den Lesevorgang abbricht?

  • Du musst bei fopen angeben, dass du die Datei zum lesen binär öffnen willst, sonst betrachtet er je nach Compiler die Datei als Text und macht dann komische Sachen mit CR, LF und eventuell NUL, EOF und ähnlichen Zeichen.


    Die Problembeschreibung wegen Offset verstehe ich nicht ganz:


    Zitat

    Sprites[offset2][offset1] offset1 kann man da leider nicht festlege, das zieht sich c ja selber wenn ich angebe 1026 bytes dann werden im offset1 die bytes 0-1026 befüllt.

    Wenn jetzt zu wenig eingelesen wird kann ich leider nicht einfach festlegen offset1 ab wert XY sollen gefüllt werden.

    Du müsstest eigentlich beliebige Orte in Sprites[a][b] wählen können und dann mit & und cast auf "Pointer auf unsigned char" an fread übergeben können. Das sollte dann ab der Stelle die gewünschte Anzahl Bytes hintereinander ins RAM lesen, welche Geometrie in Höhe und Breite deine Datenmatrix hat, braucht es dazu nicht zu wissen. Du übernimmst dann einfach über den Cast selbst die Verantwortung, dass der zusammenhängende Bereich im RAM auch der Ort ist, wo die Daten hin sollen.


    Wenn dein fread mehr Bytes lesen soll als bis Sprites[a][1025] reinpassen, dann landen die eben in Sprites[a+1 etc.][etc.] was durchaus gewünscht sein kann, wenn du bis zu 51300 Bytes am Stück anforderst. Das würde das Einlesen der Dateien stark beschleunigen.

  • Wie gesagt, gib doch (unsigned char*)&(sprites[a][b]) oder ähnlich an.


    Oder probier meinen anderen Vorschlag, wenn fread partout eindimensional denken muss:


    unsigned char huge sprites[200][1026];

    unsigned char * flatsprites = (unsigned char *)&sprites[0][0]; // nur einmal nötig, könnte aber huge benötigen


    Immer wenn du mit fread arbeiten willst, kannst du dann flatsprites statt sprites verwenden:


    ... fread( ... flatsprites[1026*a + b] ...) ...


    Ausserdem wäre da noch der Punkt, dass huge Variablen sehr ineffizient sind und du vielleicht lieber vier einzelne NICHT huge Variablen haben willst, also


    Code
    unsigned char sprites1[50][1026];
    unsigned char sprites2[50][1026];
    unsigned char sprites3[50][1026];
    unsigned char sprites4[50][1026];


    Nachdem du später oft einzelnen Sprites arbeiten willst, machst du dir einfach eine Hilfsfunktion:


    Und fürs einlesen der Grafikdateien kannst du bequem jeweils eine ganze Datei in eine ganze Variable lesen, soweit ich dich verstanden habe, arbeitest du sowieso immer mit Gruppen von 50 Sprites in einer Datei?

  • Markus ich glaube du hast da nen Knoten wegen Arrays und Pointern in C/C++. Eigentlich kannst du flat alles rausschreiben und auch wieder reinlesen. Dass du ein zweidimensionales Array gebaut hast dient ja nur dem einfacheren Zugriff. Foo[x][y] liefert ja den datentyp selber, Foo[x] hingegen einen Pointer auf den Datentyp. Eigentlich musst du eh nur Foo bei fwrite/fread angeben und als Anzahl Elemente x*y sowie sizeof(Datentyp) als Größe. Damit kannst du dein Array dumpen bzw. wieder einlesen. Vielleicht kann ich dazu morgen mal ein Beispiel schreiben…

    root42 auf YouTube


    80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, GUSar Lite & TNDY & SnarkBarker, PC MIDI Card + SC55 + MT-32, XT CF Lite, OSSC 1.6

  • Wenn ich das über einen Pointer fülle scheint es zu funktionieren, dann kann ich auch gescheit auswählen wo es landen soll.

    Muss später das ganze nochmal auf der anderen Plattform testen, Dosbox und Rechner verhalten sich ja sowieso immer etwas unterschiedlich.


    Nochmal Danke für eure Hilfe! Hoffe der 3er ist da jetzt auch was flotter, dann setze ich das überall um.

    Ich bemühe mich immer alles schön strukturiert und übersichtlich zu halten.


    Die einzelnen Bereiche sind schön in Funktionen untergebracht, man kann so gut Änderungen im Grafiksystem vornehmen ohne den Spiealablauf zu beeinflussen.

    die Steuerung ist auch separat gesteuert.

    Timings laufen auch alle schön für sich, so das es in der DOSbox von 5%-100% Geschwindigkeit sich alles exakt gleich anfühlt vom Gameplay.

  • ...wie das immer so ist. Auf einem Rechner funktioniert es einwandfrei. Auf 2 anderen nicht. Wenn ich alle 1025 einlese, haben 2 Rechner das sie nicht alles lesen.


    Ich vermute ganz stark eventuell wieder eine Compiler Einstellung?


    Kurios 2 Rechner hatten Dosbox installiert wovon es bei einem funktioniert une dem anderen nicht, der 386 funktioniert auch nicht. Bei den beiden wo es nicht funktioniert sind die Compiler einstellungen irdentisch, ich vermute der andere Rechner, kann ich erst morgen wieder prüfen, hat irgendwas anders eingestellt.

  • Ich würde gerne nochmal den Code insgesamt reviewen. Nur weil der Compiler keinen Fehler meldet, heißt das noch längst nicht, dass man nicht zum Beispiel undefiniertes Verhalten triggert, oder andere Fehler hat. Oftmals gehen dann solche merkwürdigen Fehler weg, wenn man solche Sachen aus dem Code rausklopft.

    root42 auf YouTube


    80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, GUSar Lite & TNDY & SnarkBarker, PC MIDI Card + SC55 + MT-32, XT CF Lite, OSSC 1.6

  • Das ganze ist ziemlich verrückt.


    wenn ich einzelne bytes einlese funktioniert es, wenn ich alles komplett einlese funktioniert es, wenn ich immer blöcke 1025 oder 100 oder 20 einlese funktioniert es nicht richtig.

    sowohl auf dem 386 als auch auf dem Dosbox rechner.


    Hier der code, diese Variante funktioniert.


    Wenn ich das selbe allerdings mit den 200 Sprites auf einen schlag versuche, dann macht es wieder nur noch fehler.


    das hier funktioniert nicht

  • 200 Sprites gleichzeitig sind mehr als 64 kB. Damit bekommst du das Problem, echte huge Verarbeitung zu brauchen. Huge ist wie gesagt ein ziemliches Performance-Hindernis, weil ständig Segment UND Offset berechnet werden müssen. Darum mein Vorschlag mit den 4 Variablen für jeweils 50 Sprites, die sind jeweils kleiner als 64 kB. Wenn du das Huge Memory Modell vermeiden kannst, solltest du es vermeiden.


    Der Code in deinen Bildern sagt:


    char huge *rchar = (unsigned char *) ...


    Wenn die Adresse von einem Element von MXsprite unsigned char * ist, sollte rchar das auch sein. Wenn du es vermeiden kannst, sollte rchar allerdings nicht huge sein, sondern nur far. Das fread kann dann zwar maximal 64 kB lesen, aber deine Dateien sind sowieso nur 51300 Byte pro Datei. Würde ich übrigens als 1026 * 50 schreiben, damit man sieht, woher die Zahl kommt.


    In deinem anderen Beispiel liest du immer 1025 Byte pro Schritt, aber es sollten denke ich 1026 sein. In beiden Beispielen ignorierst du den Rückgabewert von fread, der dir sagen würde, wieviele Blöcke tatsächlich gelesen worden sind, sondern machst in einem Beispiel blind "ttt += 1025" und im anderen Beispiel gehst du blind davon aus, dass alle 51300 Byte gelesen worden sind.


    Ansonsten gilt weiter der Vorschlag, switch/case statt 13 "if" hintereinander zu nehmen. Du könntest auch die Dateien von A-Z nummerieren und dann mit sehr kurzem Code auskommen:


    Code
    char * filename = "_.tex";
    if (Nr < 1 || Nr > 13) return 0;
    filename[0] = 'A' + Nr - 1;
    ifs = fopen(filename, "rb");

    Warum gibt übrigens MapTex bei Erfolg Nr zurück, aber bei Misserfolg 0? Es könnte bei Erfolg immer 1 zurückgeben und bei Misserfolg einen Wert, der die Art des Fehlers angibt.


    PS: Laut den Compilerwarnungen hast du da EINE 0106\sp0037.cpp mit mehr als 3600 Zeilen, für bessere Übersicht solltest du das Programm thematisch auf mehrere Dateien verteilen. Die Warnungen zu 3608 und 3609 sind denke ich reparaturwürdig, die anderen 2 Warnungen deuten darauf hin, dass du z.B. alten Code unvollständig entfernt hast und den Rest an der Stelle auch noch entfernen könntest, dürften aber ansonsten keine Probleme verursachen.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!