/** * Send a keepalive back to the client to confirm we have received his * * @param s the server * @param pl the player to send this to * @param ka_id the counter of the keepalived we received */ static void s_resp_keepalive(struct player *pl, uint32_t ka_id) { char *data, *ptr; int data_size = 24; data = (char *)calloc(data_size, sizeof(char)); if (data == NULL) { logger(LOG_WARN, "s_resp_keepalive : calloc failed : %s.", strerror(errno)); return; } ptr = data; wu32(0x0002bef4, &ptr); /* Function field */ wu32(pl->private_id, &ptr); /* Private ID */ wu32(pl->public_id, &ptr); /* Public ID */ wu32(pl->f4_s_counter, &ptr); /* Packet counter */ /* Checksum initialize at the end */ ptr += 4; wu32(ka_id, &ptr); /* ID of the keepalive to confirm */ /* check we filled the whole packet */ assert((ptr - data) == data_size); /* Add CRC */ packet_add_crc(data, 24, 16); sendto(pl->in_chan->in_server->socket_desc, data, 24, 0, (struct sockaddr *)pl->cli_addr, pl->cli_len); pl->f4_s_counter++; free(data); }
/** * Send the list of bans to a player * * @param s the server * @param pl the player who asked for the list of bans */ static void s_resp_bans(struct player *pl) { char *data, *ptr; int data_size, tmp_size; struct ban *b; struct server *s = pl->in_chan->in_server; size_t iter; data_size = 24; data_size += 4; /* number of bans */ ar_each(struct ban *, b, iter, s->bans) data_size += ban_to_data_size(b); ar_end_each; data = (char *)calloc(data_size, sizeof(char)); if (data == NULL) { logger(LOG_WARN, "s_resp_ban, packet allocation failed : %s.", strerror(errno)); return; } ptr = data; wu16(PKT_TYPE_CTL, &ptr); /* */ wu16(CTL_BANLIST, &ptr); /* */ wu32(pl->private_id, &ptr); /* private ID */ wu32(pl->public_id, &ptr); /* public ID */ wu32(pl->f0_s_counter, &ptr); /* packet counter */ ptr += 4; /* packet version */ ptr += 4; /* checksum */ wu32(s->bans->used_slots, &ptr);/* number of bans */ logger(LOG_INFO, "number of bans : %zu", s->bans->used_slots); ar_each(struct ban *, b, iter, s->bans) tmp_size = ban_to_data(b, ptr); ptr += tmp_size; ar_end_each; packet_add_crc_d(data, data_size); logger(LOG_INFO, "list of bans : sending %i bytes", data_size); send_to(s, data, data_size, 0, pl); pl->f0_s_counter++; free(data); }
/** * Send a "player kicked" notification to all players. * * @param s the server * @param kicker the player who kicked * @param kicked the player kicked from the server * @param reason the reason the player was kicked */ static void s_notify_kick_server(struct player *kicker, struct player *kicked, char *reason) { char *data, *ptr; struct player *tmp_pl; int data_size = 64; struct server *s = kicker->in_chan->in_server; size_t iter; data = (char *)calloc(data_size, sizeof(char)); if (data == NULL) { logger(LOG_WARN, "s_notify_kick_server, packet allocation failed : %s.", strerror(errno)); return; } ptr = data; wu16(PKT_TYPE_CTL, &ptr); /* */ wu16(CTL_PLAYERLEFT, &ptr); /* */ ptr += 4; /* private ID */ ptr += 4; /* public ID */ ptr += 4; /* packet counter */ ptr += 4; /* packet version */ ptr += 4; /* empty checksum */ wu32(kicked->public_id, &ptr); /* ID of player who left */ wu16(2, &ptr); /* visible notification : kicked */ wu32(kicker->public_id, &ptr); /* kicker ID */ wstaticstring(reason, 29, &ptr); /* check we filled all the packet */ assert((ptr - data) == data_size); ar_each(struct player *, tmp_pl, iter, s->players) ptr = data + 4; wu32(tmp_pl->private_id, &ptr); wu32(tmp_pl->public_id, &ptr); wu32(tmp_pl->f0_s_counter, &ptr); packet_add_crc_d(data, data_size); send_to(s, data, data_size, 0, tmp_pl); tmp_pl->f0_s_counter++; ar_end_each; free(data); }
/** * Send a packet to the player, indicating that his connection * was accepted. * * @param pl the player we send this packet to */ static void server_accept_connection(struct player *pl) { char *data, *ptr; int data_size = 436; struct server *s = pl->in_chan->in_server; data = (char *)calloc(data_size, sizeof(char)); if (data == NULL) { logger(LOG_WARN, "server_accept_connection : calloc failed : %s.", strerror(errno)); return; } ptr = data; wu32(0x0004bef4, &ptr); /* Function field */ wu32(pl->private_id, &ptr); /* Private ID */ wu32(pl->public_id, &ptr); /* Public ID */ wu32(pl->f4_s_counter, &ptr); /* Packet counter */ ptr += 4; /* Checksum initialize at the end */ wstaticstring(s->server_name, 29, &ptr);/* Server name */ wstaticstring(s->machine, 29, &ptr); /* Server machine */ /* Server version */ wu16(2, &ptr); /* Server version (major 1) */ wu16(0, &ptr); /* Server version (major 2) */ wu16(20, &ptr); /* Server version (minor 1) */ wu16(1, &ptr); /* Server version (minor 2) */ wu32(1, &ptr); /* Error code (1 = OK, 2 = Server Offline */ wu16(0x1FEF, &ptr); /* supported codecs (1<<codec | 1<<codec2 ...) */ ptr += 7; /* 0 = SA, 1 = CA, 2 = Op, 3 = Voice, 4 = Reg, 5 = Anonymous */ sp_to_bitfield(s->privileges, ptr); /* garbage data */ ptr += 71; wu32(pl->private_id, &ptr); /* Private ID */ wu32(pl->public_id, &ptr); /* Public ID */ wstaticstring(s->welcome_msg, 255, &ptr); /* Welcome message */ /* check we filled the whole packet */ assert((ptr - data) == data_size); /* Add CRC */ packet_add_crc(data, 436, 16); /* Send packet */ /*send_to(pl->in_chan->in_server, data, 436, 0, pl);*/ sendto(pl->in_chan->in_server->socket_desc, data, 436, 0, (struct sockaddr *)pl->cli_addr, pl->cli_len); pl->f4_s_counter++; free(data); }
/** * Refuse a connection from a player because he has been banned. * * @param cli_addr the address of the player * @param cli_len the length of cli_addr */ static void server_refuse_connection_ban(struct sockaddr_in *cli_addr, int cli_len, struct server *s) { char *data, *ptr; int data_size = 436; data = (char *)calloc(data_size, sizeof(char)); if (data == NULL) { logger(LOG_WARN, "server_refuse_connection : calloc failed : %s.", strerror(errno)); return; } ptr = data; wu32(0x0004bef4, &ptr); /* Function field */ /* *(uint32_t *)ptr = pl->private_id;*/ ptr += 4; /* Private ID */ wu32(5, &ptr); /* Public ID */ wu32(2, &ptr); /* Packet counter */ /* Checksum initialize at the end */ ptr += 4; /* *ptr = 14;*/ ptr += 1; /* Length of server name */ /* memcpy(ptr, "Nom du serveur", 14);*/ ptr += 29; /* Server name */ /* *ptr = 18;*/ ptr += 1; /* Length of server machine */ /* memcpy(ptr, "Machine du serveur", 18);*/ ptr += 29; /* Server machine */ /* Server version */ /* *(uint16_t *)ptr = 2;*/ ptr += 2; /* Server version (major 1) */ /* *(uint16_t *)ptr = 0;*/ ptr += 2; /* Server version (major 2) */ /* *(uint16_t *)ptr = 20;*/ ptr += 2; /* Server version (minor 1) */ /* *(uint16_t *)ptr = 1;*/ ptr += 2; /* Server version (minor 2) */ wu32(0xFFFFFFFA, &ptr); /* Error code (1 = OK, 2 = Server Offline, 0xFFFFFFFA = Banned */ /* rights */ ptr += 80; wu32(0x00584430, &ptr); /* Private ID */ wu32(5, &ptr); /* Public ID */ /* *ptr = 26;*/ ptr += 1; /* Length of welcome message */ /* memcpy(ptr, "Bienvenue sur mon serveur.", 26);*/ ptr += 255; /* Welcome message */ /* check we filled the whole packet */ assert((ptr - data) == data_size); /* Add CRC */ packet_add_crc(data, 436, 16); /* Send packet */ sendto(s->socket_desc, data, 436, 0, (struct sockaddr *)cli_addr, cli_len); free(data); }
extern "C" int zehn_load(NUC_FILE *file, void **mem_ptr, int (**entry)(int,char*[]), bool *supports_hww) { Zehn_header header; // The Zehn file may not begin at the file start size_t file_start = nuc_ftell(file); if(nuc_fread(&header, sizeof(header), 1, file) != 1) return 1; if(header.signature != ZEHN_SIGNATURE || header.version != ZEHN_VERSION || header.file_size > header.alloc_size) { puts("[Zehn] This Zehn file is not supported!"); return 1; } Storage<Zehn_reloc> relocs(header.reloc_count); Storage<Zehn_flag> flags(header.flag_count); Storage<uint8_t> extra_data(header.extra_size); if(nuc_fread(reinterpret_cast<void*>(relocs.data), sizeof(Zehn_reloc), header.reloc_count, file) != header.reloc_count || nuc_fread(reinterpret_cast<void*>(flags.data), sizeof(Zehn_flag), header.flag_count, file) != header.flag_count || nuc_fread(reinterpret_cast<void*>(extra_data.data), 1, header.extra_size, file) != header.extra_size) { puts("[Zehn] File read failed!"); return 1; } size_t remaining_mem = header.alloc_size - nuc_ftell(file) + file_start, remaining_file = header.file_size - nuc_ftell(file) + file_start; if(emu_debug_alloc_ptr) { if(emu_debug_alloc_size < remaining_mem) { puts("[Zehn] emu_debug_alloc_size too small!"); *mem_ptr = malloc(remaining_mem); } else *mem_ptr = emu_debug_alloc_ptr; } else *mem_ptr = malloc(remaining_mem); uint8_t *base = reinterpret_cast<uint8_t*>(*mem_ptr); if(!base) { puts("[Zehn] Memory allocation failed!"); return 1; } if(relocs.data[0].type == Zehn_reloc_type::FILE_COMPRESSED) { if(relocs.data[0].offset != static_cast<int>(Zehn_compress_type::ZLIB)) { puts("[Zehn] Compression format not supported!"); return 1; } Storage<uint8_t> compressed(remaining_file); if(nuc_fread(compressed.data, remaining_file, 1, file) != 1) { puts("[Zehn] File read failed!"); return 1; } uLongf dest_len = remaining_mem; if(uncompress(base, &dest_len, compressed.data, remaining_file) != Z_OK) { puts("[Zehn] Decompression failed!"); return 1; } std::fill(base + dest_len, base + remaining_mem, 0); } else { if(nuc_fread(base, remaining_file, 1, file) != 1) { puts("[Zehn] File read failed!"); return 1; } // Fill rest with zeros (.bss and other NOBITS sections) std::fill(base + remaining_file, base + remaining_mem, 0); } const char *application_name = "(unknown)", *application_author = "(unknown)", *application_notice = "(no notice)"; unsigned int application_version = 1, ndless_version_min = 0, ndless_version_max = UINT_MAX, ndless_revision_min = 0, ndless_revision_max = UINT_MAX; // Iterate through each flag for(Zehn_flag &f : flags) { const char *ptr; switch(f.type) { case Zehn_flag_type::EXECUTABLE_NAME: if(!zehn_check_string(extra_data.begin(), f, 255, &application_name)) { puts("[Zehn] Invalid application name!"); return 1; } break; case Zehn_flag_type::EXECUTABLE_NOTICE: if(zehn_check_string(extra_data.begin(), f, 1024, &ptr)) application_notice = ptr; break; case Zehn_flag_type::EXECUTABLE_AUTHOR: if(zehn_check_string(extra_data.begin(), f, 128, &ptr)) application_author = ptr; break; case Zehn_flag_type::EXECUTABLE_VERSION: application_version = f.data; break; case Zehn_flag_type::NDLESS_VERSION_MIN: ndless_version_min = f.data; break; case Zehn_flag_type::NDLESS_REVISION_MIN: ndless_revision_min = f.data; break; case Zehn_flag_type::NDLESS_VERSION_MAX: ndless_version_max = f.data; break; case Zehn_flag_type::NDLESS_REVISION_MAX: ndless_revision_max = f.data; break; case Zehn_flag_type::RUNS_ON_COLOR: if(f.data == false && has_colors) { msgbox("Error", "The application %s doesn't support CX and CM calculators!", application_name); return 2; } break; case Zehn_flag_type::RUNS_ON_CLICKPAD: if(f.data == false && !is_touchpad) { msgbox("Error", "The application %s doesn't support clickpads!", application_name); return 2; } break; case Zehn_flag_type::RUNS_ON_TOUCHPAD: if(f.data == false && is_touchpad) { msgbox("Error", "The application %s doesn't support touchpads!", application_name); return 2; } break; case Zehn_flag_type::RUNS_ON_32MB: if(f.data == false && (!has_colors || is_cm)) { msgbox("Error", "The application %s requires more than 32MB of RAM!", application_name); return 2; } break; case Zehn_flag_type::RUNS_ON_HWW: *supports_hww = f.data; break; default: break; } } // Show some information about the executable if(isKeyPressed(KEY_NSPIRE_CAT)) { char info[1536]; sprintf(info, "Name: %s Version: %u\nAuthor: %s\nNotice: %s", application_name, application_version, application_author, application_notice); show_msgbox("Information about the executable", info); return 2; } if(NDLESS_VERSION < ndless_version_min || (NDLESS_VERSION == ndless_version_min && NDLESS_REVISION < ndless_revision_min)) { msgbox("Error", "The application %s requires at least ndless %d.%d.%d!", application_name, ndless_version_min / 10, ndless_version_min % 10, ndless_revision_min); return 2; } if(NDLESS_VERSION > ndless_version_max || (NDLESS_VERSION == ndless_version_max && NDLESS_REVISION > ndless_revision_max)) { if(ndless_revision_max != UINT_MAX) msgbox("Error", "The application %s requires ndless %d.%d.%d or older!", application_name, ndless_version_max / 10, ndless_version_max % 10, ndless_revision_max); else msgbox("Error", "The application %s requires ndless %d.%d or older!", application_name, ndless_version_max / 10, ndless_version_max % 10); return 2; } // Iterate through the reloc table for(Zehn_reloc &r : relocs) { if(r.offset >= remaining_mem) { puts("[Zehn] Wrong reloc in Zehn file!"); return 1; } // No alignment guaranteed! uint32_t *place = reinterpret_cast<uint32_t*>(base + r.offset); switch(r.type) { //Handled above case Zehn_reloc_type::FILE_COMPRESSED: break; case Zehn_reloc_type::UNALIGNED_RELOC: if(r.offset != 0) { printf("[Zehn] Unexpected UNALIGNED_RELOC value %lu!\n", r.offset); return 1; } break; case Zehn_reloc_type::ADD_BASE: wu32(place, ru32(place) + reinterpret_cast<uint32_t>(base)); break; case Zehn_reloc_type::ADD_BASE_GOT: { uint32_t u32; while((u32 = ru32(place)) != 0xFFFFFFFF) wu32(place++, u32 + reinterpret_cast<uint32_t>(base)); break; } case Zehn_reloc_type::SET_ZERO: wu32(place, 0); break; default: printf("[Zehn] Unsupported reloc %d!\n", static_cast<int>(r.type)); return 1; } } *entry = reinterpret_cast<int (*)(int,char*[])>(base + header.entry_offset); return 0; }