WaveHC: Filenames from SD in array

Hi

I’m trying to put the filenames read from SD card into an array, but am failing so far. WaveHC is in use to read from the SD card.

Current code:

...
//filenames will be max 8 + 1 without fileending
char (*allFilenames) [9];
dir_t dirBuf;
FatReader root;
...
//count all files to initalize array
int fileCount = 0;

while(root.readDir(dirBuf) > 0) {
    fileCount++;
}
root.rewind();
allFilenames = new char [fileCount][9];

//put filenames in array
int actFile = 0;
while(root.readDir(dirBuf) > 0) {

    char actFilename[9];
    
    for(int i = 0;i < 8; i++) {
      
      actFilename[i] = dirBuf.name[i];
    }
    
    actFilename[8] = 0;
    
    strcpy(allFilenames[actFile], actFilename);
    actFile++;
  }

This overflows heavily but my C knowledge is not good enough to figure out the problem, so any help would be appreciated.
Thanks

char (*allFilenames) [9];

Why are there parentheses in this statement?

//filenames will be max 8 + 1 without fileending

Are you SURE of this? Generally, SD file names can have 8 characters, plus the dot, plus 3 characters for the extension plus the NULL. That's more than 9.

Code:
char (*allFilenames) [9];

Why are there parentheses in this statement?

I need a two-dimensional array of which only the second dimension is known beforehand. I read on a page I don’t find right now, this would be a way to do it. I’ve also tried

char **allFilenames;
...
allFilenames = (char**) malloc(fileCount * sizeof(char*));

without success.

Code:
//filenames will be max 8 + 1 without fileending

Are you SURE of this? Generally, SD file names can have 8 characters, plus the dot, plus 3 characters for the extension plus the NULL. That’s more than 9.

The filenames in dirBuf.name are without the dot and I don’t care about the extension, since it always will be WAV in my case. Hence the

for(int i = 0;i < 8; i++) {
      
      actFilename[i] = dirBuf.name[i];
}

The filenames in dirBuf.name are without the dot

But there are not always 8 characters.

Ok, I re-phrase

I'm trying to put the filenames read from SD card into an array

to

"I'm trying to put the first 8 characters of the filenames from SD card into an array"

"I'm trying to put the first 8 characters of the filenames from SD card into an array"

So, if the file name is fart.wav, you want to save all 8 of them in the array. Later, then, you'll try to replay fart.wav.wav. I get it now.

Very clever naming example, hands down.

But acutally no, because there is something like

if(dirBuf.name[i] == 87)
  break;

in the for-loop, because my filenames won't contain a "W", so I know this is were the extension starts. Can I be SURE of that? Yes. Why wasn't that in the original post? Because I saw no relevance for the actual problem, that strcpy() does not work.

Because I saw no relevance for the actual problem, that strcpy() does not work.

But, strcpy() does work.

You could use strlen() to get the length of the name of the file. You could then copy all but the last 4 (., w, a, and v). Be sure to deal with the two files in every directory named "." and "..".

I'd personally use an array of pointers, and use strdup() to copy the string to the nth pointer in the array.

char **allFileNames = NULL;

allFilenames = (char**) malloc(fileCount * sizeof(char*));

allFileNames[n] = strdup(dirBuf.name);

Thanks, strlen() was a helpful tip.

About the copying of the name, I tried this

  int actFile = 0;
  char *actFilename = NULL;
  while(root.readDir(dirBuf) > 0) {

    int actSize = 0;

    while(dirBuf.name[actSize] != 0 && dirBuf.name[actSize] != 87 && dirBuf.name[actSize] != 32) {
      actSize++;
    } 

    if(actSize > 0) {

      actSize++;
      actFilename = (char*) malloc(actSize * sizeof(char));

      for(int i=0;i < actSize-1;i++) {

        actFilename[i] = dirBuf.name[i];
      }
      
      actFilename[actSize-1] = 0;
      allFilenames[actFile] = strdup(actFilename);
      actFile++;    
    }
    free(actFilename);
  }

If I comment out

allFilenames[actFile] = strdup(actFilename);

and do Serial.print of actFilename[i] below actFilename[i] = dirBuf.name[i], it looks as it should, but with the strdup() line active, it breaks down again. So I suppose something is wrong with my actFilename?

    while(dirBuf.name[actSize] != 0 && dirBuf.name[actSize] != 87 && dirBuf.name[actSize] != 32) {

87? What the hell is 87? Wouldn't

    while(dirBuf.name[actSize] != 0 && dirBuf.name[actSize] != 'W' && dirBuf.name[actSize] != ' ') {

make more sense?

    if(actSize > 0) {
      actFilename = (char*) malloc(actSize * sizeof(char));
    }
    free(actFilename);

Conditionally allocate some memory. Unconditionally free it. Not the best idea you've had today.

The whole jerking around with actSize confuses me. You CAN call malloc() and use actSize+1 as the size argument, and quit f***ing with actSize.

So I suppose something is wrong with my actFilename?

What's wrong is that you keep allocating and freeing memory as though you had several terrabytes to waste. Use a FIXED size array for actFilename. See what that does to your problems.

87? What the hell is 87? Wouldn't
Code:

while(dirBuf.name[actSize] != 0 && dirBuf.name[actSize] != 'W' && dirBuf.name[actSize] != ' ') {

make more sense?

If printed, dirBuf.name[actSize] shows as an int (or uint8_t or whatever).

You CAN call malloc() and use actSize+1 as the size argument, and quit f***ing with actSize.

Ok, did not know that, thanks.

Use a FIXED size array for actFilename. See what that does to your problems.

I did that before the stuff above, it wasn't working either. Depending on the fixed-length, a couple of entries were correct in allFilenames but only emptyness afterwards. So I thought giving actFilename the needed size name per name would be better.

Depending on the fixed-length, a couple of entries were correct in allFilenames but only emptyness afterwards.

We’d need to see the code, and the output. But, there should be no waffling around about the size to use. The file name should be in 8.3 format, so the fixed array size, to hold the 8 characters before the . plus the terminating NULL, should be 9 elements. I never use odd lengths for arrays, and just to have a little margin, I’d use 16 for the size (so that it could hold the entire name (8 + 1 + 3 + 1) with room to spare).

I'd use 16 for the size (so that it could hold the entire name (8 + 1 + 3 + 1) with room to spare).

With this example, would it matter if the NULL is on index 12? Or could it alway be on index 15?
And would the room to spare matter for strdup() (since strlen() cannot be used as a second param there)?

With this example, would it matter if the NULL is on index 12? Or could it alway be on index 15?

Nope.

And would the room to spare matter for strdup() (since strlen() cannot be used as a second param there)?

strdup() allocates space for the string (NULL terminated array of chars) that is supplied (so, it knows the length), copies the string to that space, and returns a pointer to that space. So, if the input string is in an array of 16 chars, but only uses 3 of them, the allocated space will be enough for the three chars (and the NULL). So, no, the size of the input array doesn't matter.

Changed it the fixed-sized array:

  int fileCount = 0;

  while(root.readDir(dirBuf) > 0) {

    if(dirBuf.name[0] != 0 && dirBuf.name[0] != 87 && dirBuf.name[0] != 32)
      fileCount++;
  }

  root.rewind();

  allFilenames = (char**) malloc(fileCount * sizeof(char*));

  int actFile = 0;
  while(root.readDir(dirBuf) > 0) {

    int actLength = strlen((char*)dirBuf.name);
    char actFilename[24] = {
    };

    if(actLength < 24) {

      //copy string until 'W' or space or NULL
      int i = 0;
      while((char)dirBuf.name[i] != 'W' && (char)dirBuf.name[i] != ' ' && dirBuf.name[i] != 0) {

        actFilename[i] = (char)dirBuf.name[i];
        i++;
      }

      if(i>0) {

        actFilename[i+1] = 0;
        allFilenames[actFile] = strdup(actFilename);
        actFile++;

        for(int j=0;j<strlen(actFilename);j++)
          Serial.print(actFilename[j]);

        Serial.println("");    
      }
    }
    else {
      Serial.println("Filename too long"); 
    }
  }
  root.rewind();

  Serial.println("Filenames:");
  for(int i=0;i < fileCount;i++) {

    for(int j=0;j < strlen(allFilenames[i]);j++) {
      Serial.print(allFilenames[i][j]); 
    }
    Serial.println("");
  }
}

This is the Serial Monitor result:

DLDL
DRDR
LARB
LRAB
LURBRUD
ABABUDU
UUDU
START
RRIAB
BARLU
UABBAD
BURADLAA
DUD
UUDLL
UB
ADLRBDUU
UDLR
UUDD
UUUAAAD
JOYSTICK
6
7
8
9
ERROR
1
2
3
4
5
LLBABU
PMP_USB
Filenames:
DLDL
DRDR
LARB
LRAB
LURBRUD
ABABUDU

6

(more empty lines until fileCount)

I’m about to give up on this bullsh*t, we both invested already way too much time…

How many files are on the SD card? What are their names?

  allFilenames = (char**) malloc(fileCount * sizeof(char*));

What is the type for allFilenames?

        actFilename[i] = (char)dirBuf.name[i];

Why is it necessary to cast a char to a char?

        actFilename[i+1] = 0;

What is in actFilename after this?

        for(int j=0;j<strlen(actFilename);j++)
          Serial.print(actFilename[j]);

Have you got something against printing a string correctly?

Serial.print(actFilename);
  for(int i=0;i < fileCount;i++) {

Be nice to know what fileCount contains.

The data printed after the Filenames: line matches the crap printed before the Filenames: line, as far as I can see, except that it would appear that fileCount is hosed (perhaps because you’ve written beyond the end of an array.

Let’s make this simpler. Do NOT try to strip off the .wav from the end of the name. Do NOT implement your own copy method.

char theName[16];
strcpy(theName, entry.name);
Serial.print("theName: [");
Serial.print(theName);
Serial.println("]");

allNames[theCount++] = strdup(theName);

// After the loop, print all the names.

How many files are on the SD card? What are their names?

Their names are the crap printed before “Filenames:”

What is the type for allFilenames?

char **allFilenames = NULL;

What is in actFilename after this?

The crap.

Let’s make this simpler. Do NOT try to strip off the .wav from the end of the name. Do NOT implement your own copy method.

I have tried that. It only works with a cast:

strcpy(actFilename, (char*)dirBuf.name);

otherwise the compiler says “invalid conversion from ‘uint8_t*’ to ‘const char*’”

  int fileCount = 0;

  while(root.readDir(dirBuf) > 0) {

      fileCount++;
  }

  root.rewind();

  allFilenames = (char**) malloc(fileCount * sizeof(char*));

  int actFile = 0;
  while(root.readDir(dirBuf) > 0) {

    char actFilename[24];

    strcpy(actFilename, dirBuf.name);
     allFilenames[actFile++] = strdup(actFilename);

        Serial.print("actFilename: [");
        Serial.print(actFilename);
        Serial.println("]");
  }
  root.rewind();

  Serial.print("Filecount: ");
  Serial.println(fileCount);
  Serial.println("Filenames:");
  for(int i=0;i < fileCount;i++) {
    Serial.print(allFilenames[i]); 
    Serial.println("");
  }

Output:

actFilename: [DLDL WAV ]
actFilename: [DRDR WAV ]
actFilename: [LARB WAV ]
actFilename: [LRAB WAV ]
actFilename: [LURBRUD WAV ]
actFilename: [ABABUDU WAV Oò­ñBD]
actFilename: [UUDU WAV ]
actFilename: [START WAV 0a=ñBD]
actFilename: [RRIAB WAV ]
actFilename: [BARLU WAV _®AéBD]
actFilename: [UABBAD WAV ]
actFilename: [BURADLAAWAV 7­AéBD]
actFilename: [DUD WAV m­AéBD]
actFilename: [UUDLL WAV ]
actFilename: [UB WAV ]
actFilename: [ADLRBDUUWAV ½dóBD]
actFilename: [UDLR WAV ]
actFilename: [UUDD WAV ]
actFilename: [UUUAAAD WAV ]
actFilename: [JOYSTICKWAV ??óBD]
actFilename: [6 WAV ]
actFilename: [7 WAV ]
actFilename: [8 WAV ]
actFilename: [9 WAV]
actFilename: [ERROR WAV ]
actFilename: [1 WAV]
actFilename: [2 WAV]
actFilename: [3 WAV]
actFilename: [4 WAV]
actFilename: [5 WAV]
actFilename: [LLBABU WAV ¡lDD]
actFilename: [WINAMP~1DAT ]
actFilename: [WINAMP~1IDX ]
actFilename: [PMP_USB INI ’¦lDD]
Filecount: 34
Filenames:
DLDL WAV
…33 or so empty lines…

don’t know how the winamp-files got there, but they would have been filtered… Where the weird signs come from beats me, but I would have ignored them since they are after the ‘W’ of ‘WAV’. As I wrote, the dot does not appear in dirBuf.

You are basing this on the example that lists the files on the SS card. What does the output of that program look like?

I can't imagine why the spaces are showing up in your names, and where there are no dots in the names. I would have expected to see DLDR.WAV, DRDR.WAV, etc.

I have no explanation for the spaces, but in their example they are skipping them as well and insert the dot manually:

for (uint8_t i = 0; i < 11; i++) { // 8.3 format has 8+3 = 11 letters in it
  if (dir.name[i] == ' ')
    continue; // dont print any spaces in the name
  if (i == 8)
    Serial.print('.'); // after the 8th letter, place a dot
  Serial.print(dir.name[i]); // print the n'th digit
}

Anyway, I gave my original problem some thought and was now able to solve it without the array.
Many thanks for your time, I’ve learnt a couple of things.