Containerdateien/Virtuelle Dateisysteme



  • Scorcher24 schrieb:

    Einen Container hab ich mal vor Jahren entwickelt als ANSI-C gelernt habe.
    Ist nicht ganz so schwer, je nachdem wieviel Features man möchte.
    Ich hab im Endeffekt einen HEader aufgebaut:

    MAGIC_NUMMER - immer 99999 als Identifikation des Dateiformats

    NUM_ENTRIES(sizeof(int)) Anzahl der Eintragsblöcke

    FILENAME(sizeof(char)*255) - Der erste Dateiname mit einer fixen größe
    FILEADRESS(sizeof(int)) - Die Adresse in der Datei, bzw der Offset für fseek()
    FILELENGTH(sizeof(int)) - Die Länge der Datei

    Das wiederholt sich dann bis alle Dateien eingetragen sind. Danach werden die Dateien einfach ganz simpel hinten dran hingeschrieben. Das ist nicht besonders toll oder performat, war damals nur eine Übung. Aber zeigt doch ganz gut, wie man sowas aufbauen kann. Die Daten die man hinten dranhängt, jagt man vorher einfach nur durch inflate() in der zlib, dann ist das ganze auch komprimiert.
    Dann sollte man aber im Header auch die ungezippte Größe vermerken.

    edit: Dafür empfehle ich dir die "Goldenen Regeln der Spieleprogrammierung" von Martin Brownlow, das hat einen Abschnitt darüber mit dabei. HAb eben nachgesehen :D.
    rya.

    reicht ja schon.
    und bei programmstart liest man den header und jagt ihn in eine std::map<string,pair<FILEADRESS,FILENAME>>, dann kann man immer nach blitzschnell dateinamen suchen.



  • Ich Quote mal euch beide.

    Ich hätte ja folgendes gemacht.

    Dateichunk <- Identifikation
    Stringliste <- Dateinamen und Pfade
    chunks <-1 chunk pro Datei

    Dann hätte ich einfach die angeforderte Datei mit strcmp mit dem eintrag nacheinander verglichen.

    Alternativ könnte ich mir allerdings auch einen Baum vorstellen. Die unterpfade bekommen nummern anhand derer sich die Chunknr finden läst (da wäre dann ne simple Addition nötig) und dann springt das Programm in der Datei von Chunk zu Chunk bis es den richtigen gefunden hat. Also als bsp man braucht Dateichunk 17 also springt das programm ab den Dateichunks 17 Chunks vorwärts.

    Allerdings hab ich von Maps und Bäumen nicht so viel Ahnung, denke aber da da ein Baum schneller wäre da er den Dateipfad nachbilden kann.



  • pfade?
    du brauchst kein echtes dateisystem nachzubauen. denn dein virtuelles dateisystem ist readonly! das macht alles VIEL einfacher.



  • volkard schrieb:

    pfade?
    du brauchst kein echtes dateisystem nachzubauen. denn dein virtuelles dateisystem ist readonly! das macht alles VIEL einfacher.

    Ja es ist Readonly, aber ohne Pfade sehe ich ein kleines Problem. Mal angenommen du forderst eine Datei an "\Dateien\Texturen\Umgebung\wiese.bmp". Nun hast du ein System das erstmal schaut ob es die Datei auf der Platte gibt und danach in den Container schaut. Für die Festplatte brauchst du den Pfad. Wenn du ihn im Container wegläst und dort nur "wiese.bmp" hast dann haste das Problem das es "wiese.bmp" nur einmal geben darf, wenn es aber in verschiedenen Ordnern 2x oder öffter vorkommt wäre das Ergebniss aus dem Container das erst beste, also müsste die Datei im Container auch über den vollen Pfad abgeglichen werden, der Container wäre in diesem moment ja im Grunde nichts anderes als ein Abbild der Festplatte wenn er entpackt wäre nur das er eben eine Datei ist und Komprimiert/verschlüsselt sein kann.



  • Xebov schrieb:

    also müsste die Datei im Container auch über den vollen Pfad abgeglichen werden, der Container wäre in diesem moment ja im Grunde nichts anderes als ein Abbild der Festplatte wenn er entpackt wäre nur das er eben eine Datei ist und Komprimiert/verschlüsselt sein kann.

    ja. ich kanns mir gar nicht anders denken.



  • Dh also doch Baum der die Pfade nachbildet oder eben endlose strcmp Sessions, der Baum wäre da wohl schneller.



  • Xebov schrieb:

    Dh also doch Baum der die Pfade nachbildet oder eben endlose strcmp Sessions, der Baum wäre da wohl schneller.

    oder std::map!
    da ist doch schon ein baum drinne. ein schnelelr baum. der splittet nicht nur bei verzeichnissen, sondern irgendwo und überall, so daß der baum ausgeglichen ist.



  • Achso ok, das wusste ich nicht, ich kenn mich mit den "Lagermöglichkeiten" aus der std nicht so gut aus. Das hieße dann aber im gegenzug das ich jeder Datei eine Nummer zuweisen müsste damit dieser Baum funktioniert, sehe ich das so richtig?



  • Du kannst auch nen string nehmen. Als bsp:

    typedef std::map<std::string, std::string> myMap;
    

    Jetzt kannst du über

    myMap files;
    files["TheFolder"]
    

    die Dateien suchen.



  • Gut zu wissen. Dann wären meine Fragen soweit geklärt dann kann ich mich direkt mal in Ruhe über die Sache hermachen.



  • ich nehme mal keinen header, sondern ein indexfile der form

    img/terrein/wiese.png 0 1564
    img/terrein/pflaster.png 1564 100
    script/traktor.loa 1664 345
    

    und

    struct Beschreiber{
       int offset;
       int laenge;
    };
    ...
    //indexfile laden
    map<string,Beschreiber> lagerFiles;
    ifstream in("files.idx");
    string name;
    Beschreiber beschreiber;
    for(;;){
       file>>name;
       file>>beschreiber.offset>>beschreiber.laenge;
       if(in.eof())
          break;
       lagerfiles[name]=beschreiber;
    }
    

    und zum laden

    char* loadFile(string filename){
    Beschreiber& b=lagerFiles[filename);//ja, das ist lecker schnell!
    void* mem=operator new(beschreiber.laenge);
    offenesLagerfile.seek(beschreiber.laenge);
    offenesLagerfile.read(mem,beschreiber.laenge);
    return mem;
    }
    

    oder so. nur prinzipskizze.



  • volkard schrieb:

    ich nehme mal keinen header, sondern ein indexfile der form

    img/terrein/wiese.png 0 1564
    img/terrein/pflaster.png 1564 100
    script/traktor.loa 1664 345
    

    und

    struct Beschreiber{
       int offset;
       int laenge;
    };
    ...
    

    oder so. nur prinzipskizze.

    Ich würde es so machen:

    string
    string
    string
    

    Dann würde ich die strings einlesen und beim einlesen eine Nummer verpassen. Chunks haben den Vorteil das man sie ja überspringen kann weil sie ihre eigene Länge beinhalten. Der Vorteil an der Version ist das man wenn sich der Inhalt der Datei ändert nicht alles Aktualisieren muß, sondern man einfach den string und den Chunk an den richtigen Stellen entfernt oder einfügt, man muß somit nicht bei jeder änderung des Containers für jede Datei die Offsets aktualisieren, weil das programm sich die selbst zusammen sucht.



  • Xebov schrieb:

    Ich würde es so machen:

    auch ok. war ja nur ne skizze, um die verwendung der map zu verdeutlichen.


  • Mod

    Scorcher24 schrieb:

    Warum nicht einfach ein .zip?

    so machen es die meisten spiele. that's the way to go



  • rapso schrieb:

    Scorcher24 schrieb:

    Warum nicht einfach ein .zip?

    so machen es die meisten spiele. that's the way to go

    Was ich jetzt noch gefunden habe und was ich sehr cool finde, ist PhysFS:
    http://icculus.org/pipermail/physfs/2009-March/000697.html
    Das ist ein System zum laden von Resourcen, das jeglichen Zugriff ausserhalb der eigenen Resourcen unterbindet. Wie genau weiss ich noch nicht, aber ich habs mal geladen und werd da ein wenig reinpeeken. Vllt integrier ich das sogar bei mir.. mal schauen.
    Gute Nacht.
    rya.


  • Mod

    volkard schrieb:

    es reicht doch, wenn du aus den dateinamen fortlaufende integers machst und eine lut baust, die diese integers in fileoffsets übersetzt.

    ja, und man kann auch alle variablen einfach durchnummerieren, mit nur einem namen, foo0 bis foo65535 😮

    typischer fall von sachen, die erst kurz vorm brennen gemacht werden sollten.

    ich weiss nicht bei welchen spielen du solche erfahrungen gesammelt hast, aber von allen produkten bei denen ich mitgearbeitet habe kenne ich das man bei jedem nightly-build alle resourcen so zusammengefasst hat.
    gerade so primitive dinge sollten von anfang an implementiert und getestet sein, damit man nicht am ende boese ueberraschungen erlebt. simpelstes beispiel ist gross-kleinschreibung, je nach entwicklungsplattform ist das wichtig, essentiell oder egal. baut man am ende ein archivsystem ein was anders funktioniert (weil man was fertiges nimmt) kann das einiges an zeit dauern bis man es sauber zum laufen bekommt.
    da waere es gut es schon lange getestet zu haben.

    performancetechnisch sollte das wenig ausmachen, ob du ein mapping mit hunderten von views anguckst oder hunderte von filemappings mit je einem view. gerade das nicht-ins-ram-zwingen könnte performance bringen, könnte ich mir vorstellen.

    es bringt gerade beim oeffnen von dateien sehr viel performance, da man normalerweise alles vom OS umgeht was bremsen koennte und es bringt performance beim lesen von langsamen laufwerken, weil z.b. bei zip, die kompression kleinere dateien erlaubt.

    btw. ja ich hab gelesen was du noch geschrieben hast, und das rad was du da neu erfindest ist was .zip macht, nur sagt dein erster beitrag das irgendwie anders.



  • rapso schrieb:

    btw. ja ich hab gelesen was du noch geschrieben hast, und das rad was du da neu erfindest ist was .zip macht, nur sagt dein erster beitrag das irgendwie anders.

    ich will das entzippen vom installer erledigen lassen die daten ausgepackt auf der platte leben lassen. in einem fetten file.

    das laden einer datei will ich im prinzip weg haben. zur verdeutlichung nochmal mit indexfile und datenfile getrennt.

    //indexfile und datafile komplett in den speicher gemapt
    Range<char*,char*> loadFile(u32 fileID){
       Range<u32,u32>* descriptor=theIndexFileContent[fileID];
       return makeRange(theDataFileContent+descriptor.first,theDataFileContent+descriptor.second);
    }
    

    das macht beim file-aufsuchen O(1). und verdient darüberhinaus sogar noch das prädikat 'sauschnell'. die verwaltungskosten sind bei wenigen dutzend takten anzusiedeln. fertig und schluß.
    falls geladen werden muß, muß halt geladen werden. dann kostet es was. weil die platte lahm ist. diese kosten kommen auch auf einen zu, weil das vorher teuer ausgepackte zeug in die auslagerungsdatei ausgelagert werden mußte.
    ich hab halt quasi null verwaltungskosten, während dieses einzel-datei-geanfasse beim anwender laufzeit und programmierer entwicklungszeit kostet und bei beiden nicht gerade wenig.

    abstriche habe ich gemacht, um dem fragesteller den einsteig zu erleichtern.


  • Mod

    ja, sowas 'primitives' hat man frueher gemacht, bei konsolen sogar noch bis zur letzten generation, aber mittlerweile hat sogar ein nintendo ds ein file system. flexibilitaet und qualitaetssicherung (weil man das system immer nutzt) ist wichtiger als O(1). dateien im ram mit O(log n) suchen ist nicht dramatisch, das ist sicher um den faktor 1000schneller also eine festplatte braucht bis der lesekopf den dateianfang erreicht hat.
    und wie gesagt, kompression ist wichtig fuer schnelleren dateitransfer, auch von festplatte macht das noch sinn. natuerlich ist ein memory mapped file auf das ganze archiv einfach und simpel, aber spiele haben mittlerweile locker ein paar GB an arhieven, das bekommt man in 32bit nicht mehr gemappt.

    wegen dir hab ich total den falshback*hehe*



  • naja, der Landwirtschaft-Simulator 2008 braucht 7 sekunden bis das menu kommt, 17 sekunden, bis nach auswahl des spielstandes man was machen kann. danke, rapso!

    natürlich kann ich davon ausgehen, alles auf einmal reinzumappen. 64-bit kommt ja bald. bis dahin kann man sich behelfen zu geringen kosten.
    außerdem verlange ich mitnichten, daß ALLES in dem fetten file ist. und ich gehe davon aus, daß images als *.png im fetten file liegen, also gepackt, da ist das packen des fetten files kontraproduktiv...

    und ja, ich kann den neuesten entwicklungen nicht wirklich folgen. du hast eher das ohr am draht des zeitgeistes. ich bin beim programmieren sowas von 60-er-jahre, das gehört eigentlich auf die müllkippe.



  • Also ich muß sagen wenn ich das von euch so lese dann muß ich irgendwie an 2 alte Mänenr auf der Couch denken 😃

    rapso schrieb:

    es bringt gerade beim oeffnen von dateien sehr viel performance, da man normalerweise alles vom OS umgeht was bremsen koennte und es bringt performance beim lesen von langsamen laufwerken, weil z.b. bei zip, die kompression kleinere dateien erlaubt.

    Darf ich das dann so verstehen das du die dateien im RAM auf Lager liegen lassen würdest? Oder eher das du sie lädst und sie bis die Ladevorgänge abgeschlossen sind im RAM lässt?

    volkard schrieb:

    ich will das entzippen vom installer erledigen lassen die daten ausgepackt auf der platte leben lassen. in einem fetten file.

    das laden einer datei will ich im prinzip weg haben. zur verdeutlichung nochmal mit indexfile und datenfile getrennt.

    Nur das ich das nicht Falsch verstehe du würdest die gesammte Containerdatei so wie sie ist in den RAM schieben und von da aus dann alle Operationen durchführen lassen, statt aus dem Container eizellne Dateien rauszuladen?


Anmelden zum Antworten