Example #1
0
void file_manager::add_nfs_client(net_socket *sock)
{
  uchar size[2];
  char filename[300],mode[20],*mp;
  if (sock->read(size,2)!=2) { delete sock; return ; }
  if (sock->read(filename,size[0])!=size[0]) { delete sock; return ; }
  if (sock->read(mode,size[1])!=size[1]) { delete sock; return ; }
 

  secure_filename(filename,mode);  // make sure this filename isn't a security risk
  if (filename[0]==0) { dprintf("(denied)\n"); delete sock; return ; }

  mp=mode;
  int flags=0;

#ifdef __WATCOMC__
    flags|=O_BINARY;
#endif

  while (*mp)
  {
    if (*mp=='w') flags|=O_CREAT|O_RDWR;
    else if (*mp=='r') flags|=O_RDONLY;
    mp++;
  }

#ifdef __MAC__
  int f=open(macify_name(filename),flags);
#else      
  int f=open(filename,flags,S_IRWXU | S_IRWXG | S_IRWXO);
#endif

  FILE *fp=fopen("open.log","ab"); 
  fprintf(fp,"open file %s, fd=%d\n",filename,f);
  fclose(fp);
  
  if (f<0) 
    f=-1;  // make sure this is -1


  long ret=lltl(f);
  if (sock->write(&ret,sizeof(ret))!=sizeof(ret)) { delete sock; return ; }

  if (f<0)    // no file, sorry
    delete sock;
  else
  {
    long cur_pos=lseek(f,0,SEEK_CUR);
    long size=lseek(f,0,SEEK_END);
    lseek(f,cur_pos,SEEK_SET);
    size=lltl(size);
    if (sock->write(&size,sizeof(size))!=sizeof(size)) {  close(f); delete sock; sock=NULL; return ; }

    nfs_list=new nfs_client(sock,f,nfs_list);
    nfs_list->size=size;
  }
}
Example #2
0
int32_t file_manager::remote_file::unbuffered_seek(int32_t offset)  // tell server to seek to a spot in a file
{
  if (sock)
  {
    uint8_t cmd=NFCMD_SEEK;
    if (sock->write(&cmd,sizeof(cmd))!=sizeof(cmd)) { r_close("seek : could not send command"); return 0; }

    int32_t off=lltl(offset);
    if (sock->write(&off,sizeof(off))!=sizeof(off)) { r_close("seek : could not send offset"); return 0; }

    if (sock->read(&offset,sizeof(offset))!=sizeof(offset)) { r_close("seek : could not read offset"); return 0; }
    return lltl(offset);
  }
  return 0;
}
Example #3
0
int file_manager::process_nfs_command(nfs_client *c) {
	char cmd;
	if (c->sock->read(&cmd, 1) != 1)
		return 0;
	switch (cmd) {
	case NFCMD_READ: {
		int32_t size;
		if (c->sock->read(&size, sizeof(size)) != sizeof(size))
			return 0;
		size = lltl(size);

		c->size_to_read = size;
		return c->send_read();
	}
		break;
	case NFCMD_CLOSE: {
		return 0;
	}
		break;
	case NFCMD_SEEK: {
		int32_t offset;
		if (c->sock->read(&offset, sizeof(offset)) != sizeof(offset))
			return 0;
		offset = lltl(offset);
		offset = lseek(c->file_fd, offset, 0);
		offset = lltl(offset);
		if (c->sock->write(&offset, sizeof(offset)) != sizeof(offset))
			return 0;
		return 1;
	}
		break;
	case NFCMD_TELL: {
		int32_t offset = lseek(c->file_fd, 0, SEEK_CUR);
		offset = lltl(offset);
		if (c->sock->write(&offset, sizeof(offset)) != sizeof(offset))
			return 0;
		return 1;
	}
		break;

	default: {
		fprintf(stderr, "net driver : bad command from nfs client\n");
		return 0;
	}
	}
}
Example #4
0
file_manager::remote_file::remote_file(net_socket *sock, char const *filename, char const *mode, remote_file *Next) : sock(sock)
{
  next=Next;
  open_local=0;

  uint8_t sizes[3]={ CLIENT_NFS,strlen(filename)+1,strlen(mode)+1};
  if (sock->write(sizes,3)!=3) { r_close("could not send open info"); return ; }
  if (sock->write(filename,sizes[1])!=sizes[1]) { r_close("could not send filename"); return ; }
  if (sock->write(mode,sizes[2])!=sizes[2]) { r_close("could not send mode"); return ; }

  int32_t remote_file_fd;
  if (sock->read(&remote_file_fd,sizeof(remote_file_fd))!=sizeof(remote_file_fd))
  { r_close("could not read remote fd"); return ; }
  remote_file_fd=lltl(remote_file_fd);
  if (remote_file_fd<0) { r_close("remote fd is bad"); return ; }

  if (sock->read(&size,sizeof(size))!=sizeof(size)) { r_close("could not read remote filesize"); return ; }

  size=lltl(size);
}
Example #5
0
int32_t file_manager::remote_file::unbuffered_tell()   // ask server where the offset of the file pointer is
{
  if (sock)
  {
    uint8_t cmd=NFCMD_TELL;
    if (sock->write(&cmd,sizeof(cmd))!=sizeof(cmd)) { r_close("tell : could not send command"); return 0; }

    int32_t offset;
    if (sock->read(&offset,sizeof(offset))!=sizeof(offset)) { r_close("tell : could not read offset"); return 0; }
    return lltl(offset);
  }
  return 0;
}
void server::add_change_log(view *f, packet &pk, int number)
{
  if (f->view_changed())
  {
    uint8_t cmd=SCMD_VIEW_RESIZE;
    pk.write(&cmd,1);
    if (number)
    {
      uint16_t pn=lstl(f->player_number);
      pk.write((uint8_t *)&pn,2);
      dprintf("Server : %s resized view %d %d %d %d\n",f->name,
          f->suggest.cx1,f->suggest.cy1,f->suggest.cx2,f->suggest.cy2);
      f->resize_view(f->suggest.cx1,f->suggest.cy1,f->suggest.cx2,f->suggest.cy2);
      f->suggest.send_view=0;
    } else dprintf("sending resize to server\n");
    uint32_t view_size[8];
    view_size[0]=lltl(f->suggest.cx1);
    view_size[1]=lltl(f->suggest.cy1);
    view_size[2]=lltl(f->suggest.cx2);
    view_size[3]=lltl(f->suggest.cy2);
    view_size[4]=lltl(f->suggest.pan_x);
    view_size[5]=lltl(f->suggest.pan_y);
    view_size[6]=lltl(f->suggest.shift_down);
    view_size[7]=lltl(f->suggest.shift_right);
    pk.write((uint8_t *)view_size,8*4);
  }

  if (f->weapon_changed())
  {
    uint8_t cmd=SCMD_WEAPON_CHANGE;
    pk.write(&cmd,1);
    if (number)
    {
      uint16_t pn=lstl(f->player_number);
      pk.write((uint8_t *)&pn,2);
      dprintf("Server : %s change weapon to %d\n",f->name,f->suggest.new_weapon);
      f->current_weapon=f->suggest.new_weapon;
      f->suggest.send_weapon_change=0;
    } else dprintf("sending resize to server\n");
    uint32_t nw=lltl(f->suggest.new_weapon);
    pk.write((uint8_t *)&nw,4);
  }
}
Example #7
0
int file_manager::remote_file::unbuffered_read(void *buffer, size_t count) {
	if (sock && count) {
		uint8_t cmd = NFCMD_READ;
		if (sock->write(&cmd, sizeof(cmd)) != sizeof(cmd)) {
			r_close("read : could not send command");
			return 0;
		}

		int32_t rsize = lltl(count);
		if (sock->write(&rsize, sizeof(rsize)) != sizeof(rsize)) {
			r_close("read : could not send size");
			return 0;
		}

		int32_t total_read = 0;
		char buf[READ_PACKET_SIZE];

		ushort packet_size;
		do {
			if (sock->read(&packet_size, sizeof(packet_size))
					!= sizeof(packet_size)) {
				fprintf(stderr, "could not read packet size\n");
				return 0;
			}

			packet_size = lstl(packet_size);

			ushort size_read = sock->read(buf, packet_size);

			if (size_read != packet_size) {
				if (sock->read(buf + 2 + size_read, packet_size - size_read)
						!= packet_size - size_read) {
					fprintf(stderr, "incomplete packet\n");
					return 0;
				}
			}

			memcpy(buffer, buf, packet_size);
			buffer = (void *) (((char *) buffer) + packet_size);

			total_read += packet_size;
			count -= packet_size;
		} while (packet_size == READ_PACKET_SIZE - 2 && count);
		return total_read;
	}
	return 0;
}
view *server::add_view(packet &pk)
{
  uint32_t x[TOT_VIEW_VARS];
  if (!pk.read((uint8_t *)x,TOT_VIEW_VARS*4)) return NULL;
  for (int i=0; i<TOT_VIEW_VARS; i++) x[i]=lltl(x[i]);
  int skip=0;
  for (view *f=player_list; f; f=f->next)
    if (f->player_number==x[0])
      skip=1;

  if (skip)
  {
    pk.advance(total_objects*4);
    char nm[200];
    pk.get_string(nm,100);
    return NULL;
  }
  else
  {
    game_object *o=current_level->number_to_object(x[24]);
    if (!o)
    {
      o=create(x[25],x[26],x[27]);
      current_level->add_object(o);
    }
    view *v=new view(o,NULL,x[0]);
    o->set_controller(v);

    v->cx1=x[1];   v->cy1=x[2];   v->cx2=x[3];  v->cy2=x[4];
    v->lives=x[5];
    v->pan_x=x[6];       v->pan_y=x[7];
    v->no_xleft=x[8];    v->no_xright=x[9];  v->no_ytop=x[10];  v->no_ybottom=x[11];
    v->last_x=x[12];     v->last_y=x[13];
    v->last_left=x[14];  v->last_right=x[15]; v->last_up=x[16]; v->last_down=x[17];
    v->last_b1=x[18];    v->last_b2=x[19];    v->last_b3=x[20]; v->last_hp=x[21];
    v->last_ammo=x[22];  v->last_type=x[23]; v->visor_time=x[28]; v->current_weapon=x[29];
    v->secrets=x[30];    v->kills=x[31];

    pk.read((uint8_t *)v->weapons,total_objects*4);
    pk.get_string(v->name,100);


    return v;
  }
}
void server::distribute_changes()
{
  char cmd;

  for (view *f=player_list; f; f=f->next)
  {
    cmd=SCMD_SET_INPUT;
    next_out.write((uint8_t *)&cmd,1);
    uint16_t pn=lstl(f->player_number);
    next_out.write((uint8_t *)&pn,2);

    signed char inp[5];
    inp[0]=f->x_suggestion;
    inp[1]=f->y_suggestion;
    inp[2]=f->b1_suggestion;
    inp[3]=f->b2_suggestion;
    inp[4]=f->b3_suggestion;
    next_out.write((uint8_t *)inp,5);
  }

  if (sync_check)
  {
    cmd=SCMD_SYNC;
    uint32_t x=lltl(make_sync_uint32());
    next_out.write((uint8_t *)&cmd,1);
    next_out.write((uint8_t *)&x,4);
  }

  for (f=player_list; f; )
  {
    view *n=f->next;
    if (!f->local_player() && f->connect)
      if (!send_pkt(f->connect,next_out))
        remove_player(f);
    f=n;
  }

}
int server::join_game(out_socket *os, char *name, char *server_name)
{
  char *re="Error occurred while reading from server\n";
  packet pk;

  if (!get_pkt(os,pk))                  // read join status packet, 0 means we can't join
  { fputs(re,stderr); exit(0); }
  int32_t nfs_port;
  if (pk.read((uint8_t *)&nfs_port,4)!=4)
  { fputs(re,stderr); exit(0); }

//  connect_to_nfs_server(server_name,lltl(nfs_port));




  pk.write((uint8_t *)name,strlen(name)+1);  // send or name and see if it's ok to join in
  if (!send_pkt(os,pk))
  {
    printf("Unable to write to server\n");
    exit(0);
  }

  if (!get_pkt(os,pk))                  // read join status packet, 0 means we can't join
  { fputs(re,stderr); exit(0); }

  uint8_t stat;
  if (pk.read((uint8_t *)&stat,1)!=1)
  { fputs(re,stderr); exit(0); }

  if (stat==0)
  {
    printf("Sorry, this server is refusing you (%s)\n",name);
    exit(0);
  }


  if (current_level)
    delete current_level;

  int32_t vs[4]={ lltl(320/2-155),lltl(200/2-95),lltl(320/2+155),lltl(200/2+70)};
  pk.write((uint8_t *)vs,4*4);
  if (!send_pkt(os,pk))   { printf("Unable to write to server\n"); exit(0);  }


  current_level=new level(os);
  if (current_level->load_failed())
  {
    printf("Error occurred while downloading level\n");
    exit(1);
  }

  if (!get_pkt(os,pk))
  {
    printf("Unable to read views from server\n");
    exit(0);
  }
  uint16_t tv;
  if (pk.read((uint8_t *)&tv,2)!=2)
  { fputs(re,stderr); exit(0); }
  tv=lstl(tv);
  view *last=NULL;
  for (int i=0; i<tv; i++)
  {
    if (!get_pkt(os,pk)) { fputs(re,stderr); exit(0); }

    view *v=add_view(pk);
    if (v)
    {
      printf("added view %d\n",v->player_number);
      if (last)
        last->next=v;
      else player_list=v;
      last=v;
    } else printf("no view created, why?\n");

  }

  if (!get_pkt(os,pk)) { fputs(re,stderr); exit(0); }
  if (pk.read((uint8_t *)&rand_on,2)!=2)    // read the current random seed used by the server.
  { fputs(re,stderr); exit(0); }
  rand_on=lstl(rand_on);
  uint16_t rtab[1024];
  if (!pk.read((uint8_t *)rtab,1024*2)) { fputs(re,stderr); exit(0); }  // read the rand table

  for (int j=0; j<1024*2; j++)
    if (((uint8_t *)rtab)[j]!=((uint8_t *)rtable)[j])
    { printf("rtables differ on byte %d\n",j); exit(0); }

  if (last)
  {
    last->Drawable=1;
    last->connect=os;
  }

  start_running=1;
  is_server=0;
  return 1;
}
void server::check_for_new_players()
{
  if (is_server && has_net)
  {
    out_socket *nd=in->check_for_connect();
    if (nd)
    {
      packet pk;
//      pk.write_uint32(file_server->get_port());
      if (!send_pkt(nd,pk))
      {
    printf("error writing to connection\n");
    return ;
      }

//      while (!file_server->service_request()) milli_wait(1000);

      if (!get_pkt(nd,pk))
      {
    printf("error reading from connection\n");
    return ;
      } else
      {

    char name[100];
    pk.get_string(name,100);
    printf("Joined by player %s\n",name);
    pk.reset();
    uint8_t ok=1;
    pk.write((uint8_t *)&ok,1);      // write ok to join
    send_pkt(nd,pk);

    /**************** Read suggested view size from client ****/
    if (!get_pkt(nd,pk))
    {
      printf("error reading view info from connection\n");
      return ;
    }
    int32_t cx1,cy1,cx2,cy2;
    if (pk.read((uint8_t *)&cx1,4)!=4) return ;  cx1=lltl(cx1);
    if (pk.read((uint8_t *)&cy1,4)!=4) return ;  cy1=lltl(cy1);
    if (pk.read((uint8_t *)&cx2,4)!=4) return ;  cx2=lltl(cx2);
    if (pk.read((uint8_t *)&cy2,4)!=4) return ;  cy2=lltl(cy2);

    /**************** Create the player  *******************/
    for (view *f=player_list; f && f->next; f=f->next);      // find last player, add one for pn
    int i,st=0;
    for (i=0; i<total_objects; i++)
      if (!strcmp(object_names[i],"START"))
        st=i;

    game_object *o=create(current_start_type,0,0);
    game_object *start=current_level->get_random_start(320,NULL);
    if (start) { o->x=start->x; o->y=start->y; }
    else { o->x=100; o->y=100; }

    f->next=new view(o,NULL,f->player_number+1);
    o->set_controller(f->next);

    current_level->add_object(o);
    view *v=f->next;
    v->cx1=cx1;
    v->cy1=cy1;
    v->cx2=cx2;
    v->cy2=cy2;
    v->connect=nd;
    strcpy(v->name,name);


    if (current_level->send(nd))
    {
      uint8_t cmd=SCMD_ADD_VIEW;
      next_out.write((uint8_t *)&cmd,1);
      v->write_packet(next_out);


      /********** Send all of the views to the player **********/
      pk.reset();
      uint16_t tv=0;
      for (f=player_list; f; f=f->next) tv++;
      tv=lstl(tv);
      pk.write((uint8_t *)&tv,2);
      if (!send_pkt(nd,pk)) return ;

      for (f=player_list; f; f=f->next)
      {
        pk.reset();
        f->write_packet(pk);
        if (!send_pkt(nd,pk)) return ;
      }

      pk.reset();
      uint16_t r=lstl(rand_on);
      pk.write((uint8_t *)&r,2);       // write current random seed
      pk.write((uint8_t *)rtable,1024*2);
      send_pkt(nd,pk);

    }
      }
    }
  }
}
int server::process_command(view *f, uint8_t command, packet &pk)
{
  switch (command)
  {
    case SCMD_QUIT :                          // delete player
    {
      dprintf("Player %d has quit\n",f->player_number);
      return 0;
    } break;

    case SCMD_VIEW_RESIZE :                          // change view area
    {
      uint32_t view_size[8];
      if (pk.read((uint8_t *)view_size,8*4)!=8*4)
      return 0;
      else
      {
    f->resize_view(lltl(view_size[0]),lltl(view_size[1]),lltl(view_size[2]),lltl(view_size[3]));
    f->pan_x=lltl(view_size[4]);
    f->pan_y=lltl(view_size[5]);
    f->shift_down=lltl(view_size[6]);
    f->shift_right=lltl(view_size[7]);
    f->suggest.send_view=0;
    if (is_server)                  // if we are a server, tell everybody about this.
    {
      uint8_t cmd=SCMD_VIEW_RESIZE;
      next_out.write((uint8_t *)&cmd,1);
      uint16_t pn=lstl(f->player_number);
      next_out.write((uint8_t *)&pn,2);
      next_out.write((uint8_t *)view_size,8*4);
    }
      }
    } break;

    case SCMD_WEAPON_CHANGE :                          // change weapon
    {
      uint32_t new_weap;
      if (pk.read((uint8_t *)&new_weap,4)!=4)
        return 0;
      else
      {
    f->current_weapon=lltl(new_weap);
    f->suggest.send_weapon_change=0;
    if (is_server)                      // if we are a server, tell everybody about this.
    {
      uint8_t cmd=SCMD_WEAPON_CHANGE;
      next_out.write((uint8_t *)&cmd,1);
      uint16_t pn=lstl(f->player_number);
      next_out.write((uint8_t *)&pn,2);
      next_out.write((uint8_t *)&new_weap,4);
    }
      }
    } break;


    case SCMD_SET_INPUT :                        // set the input from this player
    {
      signed char inp[5];
      if (pk.read((uint8_t *)inp,5)!=5)
        return 0;
      else
        f->set_input(inp[0],inp[1],inp[2],inp[3],inp[4]);
    } break;

    case SCMD_ADD_VIEW :
    {
      view *v=add_view(pk);
      if (v)
      {
    for (view *f=player_list; f && f->next; f=f->next);
    if (f) f->next=v;
    else player_list=f;
      }
    } break;
    case SCMD_SYNC :
    {
      uint32_t x;
      if (pk.read((uint8_t *)&x,4)!=4)
        return 0;
      else
      {
    uint32_t s=make_sync_uint32();
    if (lltl(x)!=s)
      printf("Out of sync, %x!=%x\n",lltl(x),s);
    return 1;
      }
    } break;

    default :
      return 0;
  }
  return 1;
}
Example #13
0
int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        Usage();
        return EXIT_FAILURE;
    }

    int cmd = !strcmp(argv[2], "list") ? CMD_LIST
            : !strcmp(argv[2], "get") ? CMD_GET
            : !strcmp(argv[2], "del") ? CMD_DEL
            : !strcmp(argv[2], "put") ? CMD_PUT
            : !strcmp(argv[2], "move") ? CMD_MOVE
            : !strcmp(argv[2], "rename") ? CMD_RENAME
            : !strcmp(argv[2], "type") ? CMD_TYPE
            : !strcmp(argv[2], "getpcx") ? CMD_GETPCX
            : !strcmp(argv[2], "putpcx") ? CMD_PUTPCX
            : CMD_INVALID;

    if (cmd == CMD_INVALID)
    {
        fprintf(stderr, "abuse-tool: unknown command `%s'\n", argv[2]);
        return EXIT_FAILURE;
    }

    /* Check argument count and file access mode */
    char const *mode = "rwb";
    int minargc = 3;

    switch (cmd)
    {
    case CMD_LIST:
        mode = "rb"; // Read-only access
        break;
    case CMD_GET:
        minargc = 4;
        mode = "rb"; // Read-only access
        break;
    case CMD_PUT:
        minargc = 6;
        break;
    case CMD_MOVE:
        minargc = 5;
        break;
    case CMD_RENAME:
        minargc = 5;
        break;
    case CMD_TYPE:
        minargc = 5;
        break;
    case CMD_DEL:
        minargc = 4;
        break;
    case CMD_GETPCX:
        minargc = 4;
        mode = "rb"; // Read-only access
        break;
    case CMD_PUTPCX:
        minargc = 6;
        break;
    }

    if (argc < minargc)
    {
        fprintf(stderr, "abuse-tool: too few arguments for command `%s'\n",
                         argv[2]);
        return EXIT_FAILURE;
    }

    /* Open the SPEC file */
    char tmpfile[4096];
    char const *file = argv[1];
    snprintf(tmpfile, 4096, "%s.tmp", file);

    jFILE fp(file, mode);
    if (fp.open_failure())
    {
        fprintf(stderr, "ERROR - could not open %s\n", file);
        return EXIT_FAILURE;
    }

    spec_directory dir(&fp);

    /* Now really execute commands */
    if (cmd == CMD_LIST)
    {
        printf("   id  type    bytes   crc  name & information\n");
        printf(" ----  ----  -------  ----  ----------------------------\n");

        dir.FullyLoad(&fp);

        for (int i = 0; i < dir.total; i++)
        {
            spec_entry *se = dir.entries[i];

            /* Print basic information */
            printf("% 5i   % 3i % 8i  %04x  %s", i, se->type, (int)se->size,
                   calc_crc(se->data, se->size), se->name);

            /* Is there anything special to say? */
            switch (se->type)
            {
            case SPEC_IMAGE:
            case SPEC_FORETILE:
            case SPEC_BACKTILE:
            case SPEC_CHARACTER:
            case SPEC_CHARACTER2:
              {
                image *im = new image(&fp, se);
                printf(" \t# %i x %i pixels", im->Size().x, im->Size().y);
                delete im;
                break;
              }
            case SPEC_PALETTE:
              {
                palette *pal = new palette(se, &fp);
                printf(" \t# %i colors", pal->pal_size());
                delete pal;
                break;
              }
#if 0
            default:
              {
                /* Try to print a representation of the item */
                int has_binary = 0;
                for (int i = 0; i < Min(20, (int)se->size); i++)
                {
                    uint8_t ch = ((uint8_t *)se->data)[i];
                    if (ch < 0x20 || ch >= 0x7f)
                        has_binary++;
                }
                if (has_binary <= 2 && se->size > 5)
                    has_binary = 0;

                printf(" \t# ");
                if (!has_binary)
                    putchar('\"');

                size_t max = Min(has_binary ? 15 : 30, (int)se->size);
                for (size_t i = 0; i < max; i++)
                {
                    uint8_t ch = ((uint8_t *)se->data)[i];
                    if (has_binary)
                        printf("%02x ", ch);
                    else if (ch && (ch < 0x20 || ch >= 0x7f))
                        printf("\\x%02x", ch);
                    else if (ch)
                        putchar(ch);
                    else
                        printf("\\0");
                }
                if (se->size > max)
                    printf("...");
                else if (!has_binary)
                    printf("\"");
                break;
              }
#endif
            }

            /* Finish line */
            putchar('\n');
        }

        return EXIT_SUCCESS;
    }
    else if (cmd == CMD_GET)
    {
        int id = atoi(argv[3]);

        if (id < 0 || id >= dir.total)
        {
            fprintf(stderr, "abuse-tool: id %i not found in %s\n", id, file);
            return EXIT_FAILURE;
        }

        spec_entry *se = dir.entries[id];
        fp.seek(se->offset, SEEK_SET);

        for (size_t todo = se->size; todo > 0; )
        {
            uint8_t buf[1024];
            int step = Min((int)todo, 1024);
            fp.read(buf, step);
            fwrite(buf, step, 1, stdout);
            todo -= step;
        }
        return EXIT_SUCCESS;
    }
    else if (cmd == CMD_GETPCX)
    {
        palette *pal;
        int imgid = atoi(argv[3]);
        int palid = argc > 4 ? atoi(argv[4]) : -1;

        for (int i = 0; palid == -1 && i < dir.total; i++)
            if (dir.entries[i]->type == SPEC_PALETTE)
                palid = i;

        if (palid == -1)
            pal = new palette(256);
        else
            pal = new palette(dir.entries[palid], &fp);

        image *im = new image(&fp, dir.entries[imgid]);
        write_PCX(im, pal, "/dev/stdout");
        delete im;
        delete pal;
        return EXIT_SUCCESS;
    }
    else if (cmd == CMD_MOVE)
    {
        int src = atoi(argv[3]);
        int dst = atoi(argv[4]);

        if (src < 0 || dst < 0 || src >= dir.total || dst >= dir.total)
        {
            fprintf(stderr, "abuse-tool: ids %i/%i out of range\n", src, dst);
            return EXIT_FAILURE;
        }

        dir.FullyLoad(&fp);

        spec_entry *tmp = dir.entries[src];
        for (int d = src < dst ? 1 : -1; src != dst; src += d)
            dir.entries[src] = dir.entries[src + d];
        dir.entries[dst] = tmp;
    }
    else if (cmd == CMD_RENAME || cmd == CMD_TYPE)
    {
        int id = atoi(argv[3]);

        if (id < 0 || id >= dir.total)
        {
            fprintf(stderr, "abuse-tool: id %i out of range\n", id);
            return EXIT_FAILURE;
        }

        dir.FullyLoad(&fp);
        if (cmd == CMD_RENAME)
            dir.entries[id]->name = argv[4];
        else
            dir.entries[id]->type = (uint8_t)atoi(argv[4]);
    }
    else if (cmd == CMD_DEL)
    {
        int id = atoi(argv[3]);

        if (id < 0 || id >= dir.total)
        {
            fprintf(stderr, "abuse-tool: id %i out of range\n", id);
            return EXIT_FAILURE;
        }

        dir.total--;
        for (int i = id; i < dir.total; i++)
            dir.entries[i] = dir.entries[i + 1];

        dir.FullyLoad(&fp);
    }
    else if (cmd == CMD_PUT || cmd == CMD_PUTPCX)
    {
        int id = atoi(argv[3]);
        uint8_t type = atoi(argv[4]);

        if (id == -1)
            id = dir.total;

        if (id < 0 || id > dir.total)
        {
            fprintf(stderr, "abuse-tool: id %i out of range\n", id);
            return EXIT_FAILURE;
        }

        dir.FullyLoad(&fp);
        dir.total++;
        dir.entries = (spec_entry **)realloc(dir.entries,
                                             dir.total * sizeof(spec_entry *));
        for (int i = dir.total - 1; i-- > id; )
            dir.entries[i + 1] = dir.entries[i];

        char *name = strrchr(argv[5], '/');
        if (!name)
            name = argv[5];

        uint8_t *data;
        size_t len;

        if (cmd == CMD_PUT)
        {
            jFILE fp2(argv[5], "rb");
            if (fp2.open_failure())
            {
                fprintf(stderr, "abuse-tool: cannot open %s\n", argv[5]);
                return EXIT_FAILURE;
            }
            len = fp2.file_size();
            data = (uint8_t *)malloc(len);
            fp2.read(data, len);
        }
        else
        {
            palette *pal = NULL;
            image *im = read_PCX(argv[5], pal);
            if (!im)
            {
                fprintf(stderr, "abuse-tool: cannot open %s\n", argv[5]);
                return EXIT_FAILURE;
            }
            vec2i size = im->Size();
            len = 2 * sizeof(uint16_t) + size.x * size.y;
            data = (uint8_t *)malloc(len);
            uint16_t x = lltl((uint16_t)size.x);
            uint16_t y = lltl((uint16_t)size.y);
            memcpy(data, &x, sizeof(x));
            memcpy(data + 2, &y, sizeof(y));
            memcpy(data + 4, im->scan_line(0), size.x * size.y);
        }
        dir.entries[id] = new spec_entry(type, name, NULL, len, 0);
        dir.entries[id]->data = data;
    }
    else
    {
        /* Not implemented yet */
        return EXIT_FAILURE;
    }

    /* If we get here, we need to write the directory back */
    dir.calc_offsets();
    fp.seek(0, SEEK_SET); // FIXME: create a new file
    dir.write(&fp);
    for (int i = 0; i < dir.total; i++)
        fp.write(dir.entries[i]->data, dir.entries[i]->size);

    return EXIT_SUCCESS;
}