gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Beginners > Assets in separate files - some problems

#135083 - ShannonB - Fri Jul 20, 2007 3:56 am

So in my ongoing quest, aided by TONC, to develop Good Programming Habits (TM) from the start, I'm facing some issues.

I used to #include my image data in my main.cpp file before compiling. This, we have concluded, is a Bad Thing (TM).

I have 3 image data files to deal with:

test.map.c
test.raw.c
test.pal.c

So after reading up, I did the following.

1) commented out the #includes in my main.cpp

Code:

//include the sample tileset/map
//#include "test.pal.c"
//#include "test.raw.c"
//#include "test.map.c"


2) added the files to be compiled separately in my makefile

Code:

# --- Project details ---
PROJ    := eclipgba
EXT     := gba

CFILES  := main.cpp test.map.c test.pal.c test.raw.c
   
COBJS   := $(CFILES:.cpp=.o)
OBJS    := $(COBJS)


3) Declared the arrays that are contained in the external files, in my main.cpp just before my function prototype declarations

Code:

// ===declarations ===

extern const unsigned short test_Map[1024];
extern const unsigned short test_Palette[256];
extern const unsigned char test_Tiles[28416];


4) Commented out the declarations of the array types in the external files (since they're already declared in my main.cpp right? If I declare them twice I get an error?)

like this...
Code:

/*
 * 32x32 map data
 */
//const unsigned short
test_Map[1024] = {
0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0003, ...etc


I did the other 2 in the same way, so I'm not going to waste space and copy them here.

My c++ knowledge may be lacking, but I'm not sure how, after I've declared them, the data actually gets put into them before I use them in my code. However I can't seem to find any further steps.

When I compile I get the following error.

Code:

make -f makefile build
makefile:41: target `test.map.c' doesn't match the target pattern
makefile:41: target `test.pal.c' doesn't match the target pattern
makefile:41: target `test.raw.c' doesn't match the target pattern
test.map.c:5: warning: data definition has no type or storage class
test.pal.c:2: warning: data definition has no type or storage class
test.raw.c:5: warning: data definition has no type or storage class
d:/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/../../../../arm-eabi/bin/ld.exe: address 0x301d890 of eclipgba.elf section .data is not within region iwram
collect2: ld returned 1 exit status
make: *** [eclipgba.elf] Error 1


I have a feeling my problem might be with the makefile, but I'm really not sure. I guess the next thing is to go and do some more work on learning about makefiles?

Oh, and this was compiling and working correctly before I attempted this.
_________________
"Do you know what the chain of command is? It's the chain I get and beat you with until you understand who's in rutting command here!" -Jayne Cobb

#135086 - tepples - Fri Jul 20, 2007 4:09 am

"Not within region iwram" likely means you forgot a const somewhere, forcing a big array into .data (in IWRAM) instead of .rodata (in EWRAM or ROM).

Quote:
4) Commented out the declarations of the array types in the external files (since they're already declared in my main.cpp right? If I declare them twice I get an error?)

Which error do you get if you leave them in? I don't know of any penalty for duplicate declarations of extern arrays, just duplicate definitions.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#135091 - ShannonB - Fri Jul 20, 2007 4:24 am

Ok, I removed the commenting out for the declaration.

Now my data starts with:

Code:

const unsigned char test_Tiles[28416] = {
0xf6, 0xed, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xb0, 0x01, 0x01, 0x01, 0x01, 0x01,.......................


Ok, it compiles and works now. thanks! Guess my c++ knowledge still sucks.

I still get an odd message from the makefile, even though it works.

Code:

make -f makefile build
makefile:41: target `test.map.c' doesn't match the target pattern
makefile:41: target `test.pal.c' doesn't match the target pattern
makefile:41: target `test.raw.c' doesn't match the target pattern
copy from `eclipgba.elf' [elf32-littlearm] to `eclipgba.gba' [binary]
ROM fixed!


'doesn't match the target pattern'?
_________________
"Do you know what the chain of command is? It's the chain I get and beat you with until you understand who's in rutting command here!" -Jayne Cobb

#135096 - tepples - Fri Jul 20, 2007 4:45 am

ShannonB wrote:
I still get an odd message from the makefile, even though it works.

Code:

make -f makefile build
makefile:41: target `test.map.c' doesn't match the target pattern
makefile:41: target `test.pal.c' doesn't match the target pattern
makefile:41: target `test.raw.c' doesn't match the target pattern
copy from `eclipgba.elf' [elf32-littlearm] to `eclipgba.gba' [binary]
ROM fixed!


'doesn't match the target pattern'?

Could it have something to do with the difference between .cpp and .c?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#135107 - Ant6n - Fri Jul 20, 2007 6:54 am

On a side note, wouldnt Good Programming Habits (TM) require you to put the definitions in separate header files, i.e.

test.map.h
Code:

#define test_MapLen 2048
extern const unsigned short test_Map[1024];


test.pal.h
Code:

#define test_PaletteLen 512
extern const unsigned short test_Palette[256];


test.raw.h
Code:

#define test_TilesLen 28416
extern const unsigned char test_Tiles[28416];


of course with #ifdefs etc
or maybe put the definintions in the same header, dunno

#135108 - ShannonB - Fri Jul 20, 2007 7:02 am

Ant6n: I guess.. but wouldn't that just end up defeating the purpose of things? Now I have 3 more files in my project, with a single line of code in each one?

Dunno, could we get a ruling on this? Cearn? ...Cearn?

and Tepples, if I change the name of my data files to .cpp instead of .c files, I get the following error:

Code:

make -f makefile build
main.o: In function `main':
D:\Projects\eclipgba/main.cpp:147: undefined reference to `test_Palette'
D:\Projects\eclipgba/main.cpp:147: undefined reference to `test_Tiles'
D:\Projects\eclipgba/main.cpp:147: undefined reference to `test_Map'
collect2: ld returned 1 exit status
make: *** [eclipgba.elf] Error 1

_________________
"Do you know what the chain of command is? It's the chain I get and beat you with until you understand who's in rutting command here!" -Jayne Cobb

#135116 - keldon - Fri Jul 20, 2007 8:33 am

(a) maybe try testMap.cpp or test_map.cpp
(b) what you do in a small project may not necessarily be what you do in a larger project. But either way having a header would be the better choice, even if it's for 1 image. Although you could just have one image.h and image.cpp containing the map, palette and data.

#135124 - wintermute - Fri Jul 20, 2007 9:40 am

ShannonB wrote:
Ant6n: I guess.. but wouldn't that just end up defeating the purpose of things? Now I have 3 more files in my project, with a single line of code in each one?


It depends on how the headers are created - personally I prefer to have my data's headers and object files generated from the makefile. You'll see this method used in some of the libnds examples using grit ( currently known as git in r20 )

If you're doing things manually then there's no particular reason to favour multiple headers over a single header for all your data array prototypes.

Quote:

and Tepples, if I change the name of my data files to .cpp instead of .c files, I get the following error:

Code:

D:\Projects\eclipgba/main.cpp:147: undefined reference to `test_Palette'


add extern to the declarations in the .cpp files

See http://www.kuzbass.ru:8086/docs/isocpp/dcl.html

isocpp wrote:

7.1.1 - Storage class specifiers [dcl.stc]

-6- A name declared in a namespace scope without a
storage-class-specifier has external linkage unless it has internal linkage
because of a previous declaration and provided it is not declared const.
Objects declared const and not explicitly declared extern have internal
linkage.

_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#135127 - Cearn - Fri Jul 20, 2007 10:04 am

ShannonB wrote:
2) added the files to be compiled separately in my makefile

Code:

# --- Project details ---
PROJ    := eclipgba
EXT     := gba

CFILES  := main.cpp test.map.c test.pal.c test.raw.c
   
COBJS   := $(CFILES:.cpp=.o)
OBJS    := $(COBJS)



The COBJS line takes the CFILES list and turns all ".cpp" extensions into ".o" extensions. test.map.c and such do not have a .c extension and are therefore excluded. Renaming to ".cpp" will fix that, but this causes other problems, namely these:

ShannonB wrote:
Code:
make -f makefile build
main.o: In function `main':
D:\Projects\eclipgba/main.cpp:147: undefined reference to `test_Palette'
D:\Projects\eclipgba/main.cpp:147: undefined reference to `test_Tiles'
D:\Projects\eclipgba/main.cpp:147: undefined reference to `test_Map'
collect2: ld returned 1 exit status
make: *** [eclipgba.elf] Error 1


This is one of those areas where something that works in C, doesn't quite work in C++. In C++, const data is considered static, unless you add an 'extern' declaration in the datafile as well. What this means is that unless you do something like this:

Code:
// foo.cpp : A file with data arrays

extern const char fooData[];    // External declaration !!

const char fooData[]= {...};     // Data definition.

fooData will not be visible outside of foo.cpp

Instead of renaming the datafiles to .cpp, either use pure C for all (only works if there aren't any C++-y things in your code), or split the rules into C and CPP items. C and CPP need different compilation tools anyway, so this might be the way to go here.

Code:

# Makefile Rules for C and C++ code.
CFILES   := test.map.c test.raw.c test.pal.c
CPPFILES := main.cpp

COBJS   := $(CFILES:.c=.o)
CPPOBJS :=  $(CPPFILES:.c=.o)
OBJS    := $(COBJS) $(CPPOBJS)

CFLAGS   := ...
CPPFLAGS := ...

# etc

Or, instead of altering your current makefile, you can use devkitPro's template makefiles where all of this is already done for you.

ShannonB wrote:
Ant6n: I guess.. but wouldn't that just end up defeating the purpose of things? Now I have 3 more files in my project, with a single line of code in each one?

The problem with #including data is that it would need recompiling every time you do anything inside main.cpp. But this is only required if the definitions of the data are #included. The external thingies are declarations, which just tell the compiler that something of a given name exists somewhere in the project.

You don't have to have one additional file for each data-file either. If you prefer, you can dump all the declarations into a single file. As Wintermute hinted, grit can also create the header files for you.

Note: because of C++'s name-mangling, you need to put extern "C" { ... } around the declarations for the things in the C files, otherwise the linker will look for the wrong names:
Code:
// in main.cpp. I may be off in the syntax a little here.

extern "C" {
#include "test.pal.h"
#include "test.raw.h"
#include "test.map.h"
};