Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411492 Posts in 69377 Topics- by 58433 Members - Latest Member: Bohdan_Zoshchenko

April 29, 2024, 07:58:28 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Reading sequential files, OH MY
Pages: [1]
Print
Author Topic: Reading sequential files, OH MY  (Read 1819 times)
Ben_Hurr
Level 10
*****


nom nom nom


View Profile
« on: February 03, 2010, 12:48:10 PM »

Alright duders,
if you had to load a dozen sequentially named files (ie durr01.dur, durr02.dur, durr03.dur etc) how would you go about it?
How would you reject non-matching files in a way that isn't easily broken? Cool


edit:
I'm using BlitzMax to do this. :c
« Last Edit: February 03, 2010, 02:00:52 PM by Ben_Hurr » Logged
brog
Level 7
**



View Profile WWW
« Reply #1 on: February 03, 2010, 01:01:44 PM »

Uh..
Code:
char buffer[BUFFER_SIZE];
for (int i=1; i<=12; ++i)
{
  sprintf(buffer, "durr%2d.dur", i);
  ifstream f(buffer);
  //do stuff with f
}
I guess.  But what's your platform?

And I'm not sure what you mean by "reject non-matching files".  Do you mean files that are missing from the sequence, or what?
Logged
mewse
Level 6
*



View Profile WWW
« Reply #2 on: February 03, 2010, 01:03:19 PM »

Code:
FILE *file;
char filename[20];
int i = 1;
do
{
  snprintf(filename,20,"durr%02d.durr", i);  // build filename
  filename[19] = 0;                          // set string terminator (in case string was too long for snprintf to add it)
  i++;
  file = fopen(filename, "r");
  if ( file )
  {
    //process file here
  }
}while(file);

The magic is done inside snprintf, which formats the "durr01.durr" filename strings for each file in sequence.
« Last Edit: February 03, 2010, 01:06:24 PM by mewse » Logged
Ben_Hurr
Level 10
*****


nom nom nom


View Profile
« Reply #3 on: February 03, 2010, 01:29:50 PM »

Yeeeeeeah C++ isn't going to work here, because I'm using BlitzMax.
So I'm going to have to play with strings directly.  Cry

But as to your question about rejection, I don't want to bother trying to open files that obviously don't match my naming convention, that's all.
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #4 on: February 03, 2010, 03:33:47 PM »

You have two answers, just adapt them to Blitz already will ya. Or get a blitz IO tutorial somewhere Wink  
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
gnat
Level 1
*



View Profile WWW
« Reply #5 on: February 03, 2010, 03:53:31 PM »

Also if load times get too high, you may want to consider zipping them and loading from the one big archive file.
Logged

LAN Party List - The definitive LAN party list. Also Game Jams, etc.
GitHub
Ben_Hurr
Level 10
*****


nom nom nom


View Profile
« Reply #6 on: February 03, 2010, 03:56:06 PM »

You have two answers, just adapt them to Blitz already will ya. Or get a blitz IO tutorial somewhere Wink  

Already done, actually!
Now I just need to pad out a string with zeros haha
Logged
nikki
Level 10
*****


View Profile
« Reply #7 on: February 03, 2010, 04:39:11 PM »

couldn't you do something like this (blitzmax pseudo code)
Code:
superstrict

function read_file(filename)
' readbyte etc.
'
endfunction



for local i:int = 0 until number_files
  local str:string
  str:+"durr"
  if i<10 then str:+"0"
  str:+i
  str:+".dur"
  read_file(str)

next


i am tired, so i havent tested this. but for an example its ok i guess hope Smiley
Logged
Ben_Hurr
Level 10
*****


nom nom nom


View Profile
« Reply #8 on: February 03, 2010, 05:19:18 PM »

i am tired, so i havent tested this. but for an example its ok i guess hope Smiley
Actually, I think I solved it.  Smiley
Code:
'//
'//  ROOM NUMBERING
'//
'  Find a consistant way of loading and numbering levels consecutively
superstrict

const FILE_PATH:string   = "maps/" '// folder to look for files in
const FILE_PREFIX:string = "room"  '// 1st part of file name - all room files need to start
                                   '   with this prefix to work.
const FILE_EXT:string    = "map"   '// 3rd part of file name - extension needs to match
                                   '   to work as well.

local room:string[0] 'room array - each index corresponds to a room

'Make x amount of dummy level files named sequentially
'Format = 'room####.map'
for local i:int = 0 to 4
local num:string = i

'pad out num string with 0's if too short
if(num.length < 4)
num = "000"[..4-num.length]+num
end if

local file:tstream = writestream(FILE_PATH+FILE_PREFIX+num+"."+FILE_EXT)
writeline(file, num) 'put room index in file
closestream(file)
next

'Load up each one in the map folder and assign them an index based on their filename
'Only recognizes files in this format >> 'room####.map', where #### represents numbers
local files:string[] = loaddir(FILE_PATH)

for local url:string = eachin files
local ind:int = IsValid(url)

'Add to array if file is valid
'File # determines where it's stuffed in the array
if(ind>=0)
room      = room[..ind+1] 'expand array as necessary
room[ind] = stripdir(url)
else
print "'"+url+"' Rejected!"
end if
next

'Run through the room array to see how it did
for local i:int = 0 to room.length-1
print "room["+i+"] ="+room[i]
next

end




'// Checks if a file is valid as a mapfile
'// Returns the maps index number if valid, otherwise returns -1
'  url = File path
function IsValid:int( url:string )
local fname:string = stripall(url).tolower()   'case independant filename
local num:string   = fname[4..8]               'room index is 4 chars in - 4 chars long
local ext:string   = extractext(url).tolower() 'case independant file ext.

'check if filename is the right size (it should be 8 characters)
if(fname.length <> 8) return -1

'check if extension is correct
if(ext <> FILE_EXT) return -1

'check if beginning of filename is 'room'
if(fname.startswith(FILE_PREFIX) = false) return -1

'determine if num string contains *only* numbers
for local i:byte = 0 to num.length-1
if(num[i]<48 or num[i]>57) return -1 '// ASCII chars 48-57 are numbers 0-9
next

return num.toint() '// if all checks out, return room index
end function

Hopefully this might help someone else doing this. =O
Logged
nikki
Level 10
*****


View Profile
« Reply #9 on: February 03, 2010, 05:52:55 PM »

i like that isvalid method, will have a closer look one of these days Smiley

is there a good reason your using arrays instead of linked lists ?
because always when i see resizing of arrays i think linked list, but maybe that's just me..

but anyway cool that you figured it out!
Logged
Ben_Hurr
Level 10
*****


nom nom nom


View Profile
« Reply #10 on: February 03, 2010, 06:06:22 PM »

Oh yes, I'm using an array because that's how my room system will work:
Each index in the array will hold a room object, and the game can switch the active room by using that index. Smiley
The list of rooms wouldn't change during the game, so arrays were ideal.

I also kinda wanted to automate filling that array with map files so I don't have to hardcode filepaths in. Shrug
Logged
nikki
Level 10
*****


View Profile
« Reply #11 on: February 04, 2010, 02:15:51 AM »

I suspect these are the 2kb rooms you were loading in during playtime earlier ?
tell us, how long does it take to read in , well lets say 1000 rooms, at loadtime.

And is that fast enough for your wishes? Ninja Ninja

Logged
Ben_Hurr
Level 10
*****


nom nom nom


View Profile
« Reply #12 on: February 04, 2010, 08:30:20 AM »

At the moment, loading 1000 1x1 rooms takes about 0.5-2 seconds for me. Gentleman

Code:
'//
'//  LEVEL LOADING TEST
'//
superstrict

local ft:int
local map:TLevel = TLevel.create(1,1,1,1,15) 'max size is 5x5
print "Saving map..."
map.save("level.dat")
print "...Map saved!"
'debugstop() 'debugging to inspect object

ft  = millisecs()
print "Loading map..."
for local i:int = 1 to 1000 'load multiple times to simulate loading multiple maps - important to see ON STARTUP loading times
map = TLevel.load("level.dat")
next
print "...Map loaded!"
'debugstop() 'debugging to inspect object
ft  = millisecs() - ft

print ft+"ms"

end



'Object that represents a tilebased level in a videogame
type TLevel
field tileset:byte
field music:byte
field w:byte, h:byte
field bg:TBg
field tlayer0:byte[0,0]
field tlayer1:byte[0,0]
field tlayer2:byte[0,0]
field mlayer:byte[0,0]
field obj_list:TDummy[]

'Make a new empty level
'  tileset = tileset id
'  music   = music id
'  w,h     = map dimensions in screens
'  num_boj = number of dummy objects in the map
function create:TLevel( tileset:byte, music:byte, w:byte, h:byte, num_obj:int )
local obj:TLevel = new TLevel
obj.tileset  = tileset
obj.music    = music
obj.w        = w
obj.h        = h
obj.bg       = TBg.create(1,1,1,1,1)
obj.tlayer0  = new byte[w*20, h*15]
obj.tlayer1  = new byte[w*20, h*15]
obj.tlayer2  = new byte[w*20, h*15]
obj.mlayer   = new byte[w*20, h*15]
obj.obj_list = new TDummy[num_obj]

local i:int
for i = 0 to len(obj.obj_list)-1
obj.obj_list[i] = TDummy.create(1,1,1)
next

return obj
end function

'Recreate a level from a file
'  url = Filepath to save level to
function load:TLevel( url:string )
local lev:TLevel = new TLevel
local file:tstream = readstream(url)

'// Load general level settings
lev.tileset = readbyte(file) '// tileset index byte
lev.music   = readbyte(file) '// music index byte
lev.w       = readbyte(file) '// level width byte
lev.h       = readbyte(file) '// level height byte

'// Load BG settings
lev.bg = new TBg              '// make a new BG object
lev.bg.id   = readbyte(file)  '// BG image index byte
lev.bg.x    = readfloat(file) '// BG x offset float
lev.bg.y    = readfloat(file) '// BG y offset float
lev.bg.xvel = readfloat(file) '// BG xvel float
lev.bg.yvel = readfloat(file) '// BG yvel float

'// Load Tile layer 0
lev.tlayer0 = new byte[20*lev.w, 15*lev.h]

local x:int, y:int
for y = 0 to (15*lev.h)-1
for x = 0 to (20*lev.w)-1
lev.tlayer0[x, y] = readbyte(file) '// current tile value byte
next
next

'// Load Tile layer 1
lev.tlayer1 = new byte[20*lev.w, 15*lev.h]

for y = 0 to (15*lev.h)-1
for x = 0 to (20*lev.w)-1
lev.tlayer1[x, y] = readbyte(file) '// current tile value byte
next
next

'// Load Tile layer 2
lev.tlayer2 = new byte[20*lev.w, 15*lev.h]

for y = 0 to (15*lev.h)-1
for x = 0 to (20*lev.w)-1
lev.tlayer2[x, y] = readbyte(file) '// current tile value byte
next
next

'// Load Mask layer
lev.mlayer = new byte[20*lev.w, 15*lev.h]

for y = 0 to (15*lev.h)-1
for x = 0 to (20*lev.w)-1
lev.mlayer[x, y] = readbyte(file) '// current tile value byte
next
next

'// Load object list
lev.obj_list = new TDummy[readshort(file)] '// list size byte

local i:int
for i = 0 to len(lev.obj_list)-1
local obj:TDummy = new TDummy '// make new blank object
obj.id      = readshort(file) '// current object's id short
obj.x       = readfloat(file) '// current object's x float
obj.y       = readfloat(file) '// current object's y float
lev.obj_list[i] = obj         '// put object in list
next

closestream(file) '// close file once finished

return lev
end function

'Write the level to disk
'  url = filepath of level file
method save:TLevel( url:string )
local file:tstream = writestream(url)

'// Save general level settings
writebyte(file, tileset)  '// tilset byte
writebyte(file, music)    '// music byte
writebyte(file, w)        '// level width byte
writebyte(file, h)        '// level height byte

'// Save BG settings
writebyte(file, bg.id)    '// BG image index byte
writefloat(file, bg.x)    '// BG x offset float
writefloat(file, bg.y)    '// BG y offset float
writefloat(file, bg.xvel) '// BG xvel float
writefloat(file, bg.yvel) '// BG yvel float

'// Save Tile layer 0
local x:int, y:int
for y = 0 to (15*h)-1
for x = 0 to (20*w)-1
writebyte(file, tlayer0[x, y]) '// each byte in the array
next
next

'// Save Tile layer 1
for y = 0 to (15*h)-1
for x = 0 to (20*w)-1
writebyte(file, tlayer1[x, y]) '// each byte in the array
next
next

'// Save Tile layer 2
for y = 0 to (15*h)-1
for x = 0 to (20*w)-1
writebyte(file, tlayer2[x, y]) '// each byte in the array
next
next

'// Save Mask layer
for y = 0 to (15*h)-1
for x = 0 to (20*w)-1
writebyte(file, mlayer[x, y]) '// each byte in the array
next
next

'// Save object list
writeshort(file, len(obj_list)) '// list size byte

'// MARK: IT'd probably be better to make read/write methods for each object that needs saving
local obj:TDummy
for obj = eachin obj_list
writeshort(file, obj.id) '// current object's id byte
writefloat(file, obj.x)  '// current object's x float
writefloat(file, obj.y)  '// current object's y float
next

closestream(file) '// close file once finished
end method
end type

'Object that represents a level's background layer
type TBg
field id:byte
field x:float, y:float
field xvel:float, yvel:float

function create:TBg( id:byte, x:float, y:float, xvel:float, yvel:float )
local obj:TBg = new TBg
obj.id   = id
obj.x    = x
obj.y    = y
obj.xvel = xvel
obj.yvel = yvel

return obj
end function
end type

'Object that represents an object in the level
type TDummy
field id:short
field x:float, y:float

function create:TDummy( id:short, x:float, y:float )
local obj:TDummy = new TDummy
obj.id = id
obj.x  = x
obj.y  = y

return obj
end function
end type
Hoho, try it for yourself?
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic