Dane binarne z wielu plików w jednym

Dane binarne z wielu plików w jednym
0

Dzień dobry.

Podczas pisania aplikacji natknąłem się na problem, w którym moja znajomość języka C++ i umiejętności korzystania z google są niewystarczające. Próbowałem już wiele kombinacji, przez co kod wygląda jak wygląda...

No to od początku... Mam sobie nie mój kod, który dekoduje pliki PNG z podanego vector'a. Przy normalnym ładowaniu z pliku procedura wygląda tak:

Kopiuj
int decodePNG(std::vector<unsigned char>& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true)
{
  ...
}

void loadFile(std::vector<unsigned char>& buffer, const std::string& filename) //designed for loading files from hard disk in an std::vector
{
  std::ifstream file(filename.c_str(), std::ios::in|std::ios::binary|std::ios::ate);

  //get filesize
  std::streamsize size = 0;
  if(file.seekg(0, std::ios::end).good()) size = file.tellg();
  if(file.seekg(0, std::ios::beg).good()) size -= file.tellg();

  //read contents of the file into the vector
  if(size > 0)
  {
    buffer.resize((size_t)size);
    file.read((char*)(&buffer[0]), size);
  }
  else buffer.clear();
}

int main(int argc, char *argv[])
{
  const char* filename = argc > 1 ? argv[1] : "test.png";
  
  //load and decode
  std::vector<unsigned char> buffer, image;
  loadFile(buffer, filename);
  unsigned long w, h;
  int error = decodePNG(image, w, h, buffer.empty() ? 0 : &buffer[0], (unsigned long)buffer.size());

  return 0;
}

Po takim zastosowaniu funkcji decodePNG mogę sobie vector image oraz szerokość i wysokość obrazu (w, h) wykorzystać przy ładowaniu tekstur OpenGL:

Kopiuj
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &image[0]);

Ten etap jest zamknięty, ładowanie pojedynczych plików png działa wyśmienicie. Ale teraz chcę to przerobić, aby składować dane binarne wszystkich tekstur skompresowanych PNG w jednym pliku i żeby ładowanie odbywało się przy minimalnej ingerencji w resztę kodu aplikacji. Tak więc modyfikowana będzie tylko funkcja loadFile, ale najpierw opiszę sam zapis plików png w jeden plik. Odbywa się to małą oddzielną aplikacją napisaną na potrzeby projektu:

Kopiuj
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
/* "readdir" etc. are defined here. */
#include <dirent.h>
/* limits.h defines "PATH_MAX". */
#include <limits.h>

#include <iostream>
#include <fstream>

using namespace std;

std::ofstream outf("data.bin", std::ios::out|std::ios::binary|std::ios::trunc);

bool hasEnding (string const &fullString, string const &ending)
{
    if (fullString.length() >= ending.length()) {
        return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
    } else {
        return false;
    }
}

void decision(string dir)
{
	if (hasEnding(dir,".png"))
	{
		cout << dir << endl;
		
		outf.write(dir.c_str(),dir.length());
		outf.write("\n",1);
		ifstream file(dir.c_str(), ios::in|ios::binary);
		char buff[1];
		while (!file.eof())
		{
			file.read(buff,1);
			outf.write(buff,1);
		}
		file.close();
		outf.write("\n",1);
	}
}

/* List the files in "dir_name". */

static void
list_dir (const char * dir_name)
{
    DIR * d;

    /* Open the directory specified by "dir_name". */

    d = opendir (dir_name);

    /* Check it was opened. */
    if (! d) {
        fprintf (stderr, "Cannot open directory '%s': %s\n",
                 dir_name, strerror (errno));
        exit (EXIT_FAILURE);
    }
    char a[512];
    while (1) {
        struct dirent * entry;
        const char * d_name;

        /* "Readdir" gets subsequent entries from "d". */
        entry = readdir (d);
        if (! entry) {
            /* There are no more entries in this directory, so break
               out of the while loop. */
            break;
        }
        d_name = entry->d_name;
        /* Print the name of the file and directory. */
        //printf ("%s/%s\n", dir_name, d_name);
        
        strcpy(a,"");
        strcat(a,dir_name);
        strcat(a,"/");
        strcat(a,d_name);
        //printf ("%s\n",a);
        decision(a);
        

        /* See if "entry" is a subdirectory of "d". */

        if (entry->d_type & DT_DIR) {

            /* Check that the directory is not "d" or d's parent. */
            
            if (strcmp (d_name, "..") != 0 &&
                strcmp (d_name, ".") != 0) {
                int path_length;
                char path[PATH_MAX];
 
                path_length = snprintf (path, PATH_MAX,
                                        "%s/%s", dir_name, d_name);
                //printf ("%s\n", path);
                if (path_length >= PATH_MAX) {
                    fprintf (stderr, "Path length has got too long.\n");
                    exit (EXIT_FAILURE);
                }
                /* Recursively call "list_dir" with the new path. */
                list_dir (path);
            }
        }
    }
    /* After going through all the entries, close the directory. */
    if (closedir (d)) {
        fprintf (stderr, "Could not close '%s': %s\n",
                 dir_name, strerror (errno));
        exit (EXIT_FAILURE);
    }
}

int main ()
{
    list_dir ("data");
    outf.close();
    return 0;
}

Funkcja list_dir jest nieważna, wzięta gdzieś z neta, w typ przypadku podaje ona wszystkie pliki z drzewa katalogów (zaczynającego się od "korzenia" data) do decyzji funkcji decision, jeśli to plik ma rozszerzenie png, to będzie dopisywany do pliku data.bin (strumień outf). Zapis prezentuje się następująco:

Kopiuj
sciezka/do/pliku.png\n
dane_binarne_pliku_png\n
sciezka/do/pliku2.png\n
dane_binarne_pliku_png_2\n
sciezka/do/pliku3.png\n
dane_binarne_pliku_png_3\n

Po zapisie wszystkich plików, rozmiar pliku data.bin ma rozmiar około taki jak wszystkie pliki png w podkatalogach folderu data.

No i teraz w głównej aplikacji chcę sobie ładować plik png znajdujący się pod "identyfikatorem", czyli ścieżką do pliku. Wszelkie próby kończą się na błędzie zwracanym przez funkcję decodePNG. Z moich prób debugowania wynika, że do vectora buffer idzie nieodpowiednia ilość danych. Oto funkcja loadFile, którą chcę podmienić:

Kopiuj
void Texture::loadFile(std::vector<unsigned char>& buffer, const std::string& filename) //designed for loading files from hard disk in an std::vector
{
	std::ifstream file("data.bin", std::ios::in|std::ios::binary);

	char crsr;

	while (!file.eof()) {
		file >> crsr;

		if (crsr=='d') {
			bool good=true;
			for (unsigned int i=1; i<filename.length(); i++) {
				file >> crsr;
				if (filename[i]!=crsr) {
					good=false;
					break;
				}
			} // po sprawdzenu czy nazwa zaczynajaca sie na 'd' (data) jest kluczem, ktorego szukamy
			// laduje plik
			if (good) {
				while (crsr!='\n' && !file.eof()) {
					buffer.push_back(crsr);
					file >> crsr;
				}
				cout << buffer.size();
			}
		}
	}

	file.close();
}
K9
Tak z ciekawości, co cię powstrzymuje przed po prostu wsadzeniem wszystkich plików do TAR-a/ZIP-a?
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0

Czy możesz zmienić format tego pliku "data.bin"?


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
0

No pewnie, że tak ;) Prosiłbym o poradę, jak to zrobić najlepiej, żeby wczytywanie działało i w miarę wyglądało. Bo zdaję sobie sprawę, że teraz to strasznie "lame".

kult
  • Rejestracja:prawie 22 lata
  • Ostatnio:ponad rok
0

Po prostu nie szukaj czegoś co może wystąpić w danych binarnych pliku, zrób np tak:

sciezka/do/pliku.png\n
ilość_bajtów_danych_binarnych\n
dane_binarne_pliku_png\n
sciezka/do/pliku2.png\n
ilość_bajtów_danych_binarnych\n
dane_binarne_pliku_png_2\n
sciezka/do/pliku3.png\n
ilość_bajtów_danych_binarnych\n
dane_binarne_pliku_png_3\n

W takim przypadku z góry wiesz ile bajtów odczytać.

_13th_Dragon
Mieszanie formatu binarnego z tekstowym, na serio?
kult
tak, na serio, nie widzę w tym nic złego.
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
1
Kopiuj
                unsigned len=dir.length();
                outf.write((char*)&len,sizeof(unsigned));
                outf.write(dir.c_str(),len);
                ifstream file(dir.c_str(),ios::in|ios::binary);
                file.seekg(0,ios::end);
                streamsize size=file.tellg();
                file.seekg(0,ios::beg);
                outf.write((char*)&size,sizeof(streamsize));
                while(size)
                   {
                    char buff[1024];
                    unsigned cnt=file.read(buff,1024).gcount();
                    outf.write(buff,cnt);
                    size-=cnt;
                  }
                file.close();

Przy takim zapisie możesz dosyć szybko iterować zawartość pliku.

Kopiuj
void Texture::loadFile(vector<unsigned char>& buffer,const string& filename)
  {
   ifstream file("data.bin",ios::in|ios::binary);
   unsigned len;
   while(file.read((char*)&len,sizeof(unsigned)))
     {
      string currname(len,'\0');
      file.read(&currname[0],len);
      streamsize size;
      file.read((char*)&size,sizeof(streamsize));
      if(currname==filename)
        {
         buffer.resize(size);
         buffer.read((char*)&buffer[0],size);
         return;
        }
      fille.seekg(size,ios::cur);
     }
  }

Pisano na sucho, mogą być błędy, traktować jako schemat.


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
0

Niesamowite, działa prawie bez ingerencji z mojej strony.

buffer.read - powinno być file.read
fille.seekg - literówka, powinno być file.seekg ;)

Ale to wszystko zrozumiałe, skoro jest pisane z palca na szybko. Jesteś super trzynasty smoku ;)

Tak z ciekawości, co cię powstrzymuje przed po prostu wsadzeniem wszystkich plików do TAR-a/ZIP-a?

Po pierwsze, żeby nie dołączać niepotrzebnie biblioteki do projektu. Po drugie, żeby pierwszy lepszy typek nie podmienił grafik w mojej grze i żeby mieć furtkę na przyszłość i sobie w jakiś prosty sposób dane zabezpieczyć, pomieszać itp.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)