diff options
author | Paweł Kołodziejski | 2006-05-23 23:43:52 +0000 |
---|---|---|
committer | Paweł Kołodziejski | 2006-05-23 23:43:52 +0000 |
commit | 107073537e4ad24e294e28a2a62f8d3ad33008ff (patch) | |
tree | 9eade8ff944f701d2676aa2ddd94e3585be9c813 /engines/agi/savegame.cpp | |
parent | 3a025038da6d351789d5daf29e4751678c47fed1 (diff) | |
download | scummvm-rg350-107073537e4ad24e294e28a2a62f8d3ad33008ff.tar.gz scummvm-rg350-107073537e4ad24e294e28a2a62f8d3ad33008ff.tar.bz2 scummvm-rg350-107073537e4ad24e294e28a2a62f8d3ad33008ff.zip |
imported AGI engine
svn-id: r22588
Diffstat (limited to 'engines/agi/savegame.cpp')
-rw-r--r-- | engines/agi/savegame.cpp | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/engines/agi/savegame.cpp b/engines/agi/savegame.cpp new file mode 100644 index 0000000000..e4efc4402f --- /dev/null +++ b/engines/agi/savegame.cpp @@ -0,0 +1,790 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * Copyright (C) 1999-2003 Sarien Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* + * Savegame support by Vasyl Tsvirkunov <vasyl@pacbell.net> + * Multi-slots by Claudio Matsuoka <claudio@helllabs.org> + */ + +#include "common/stdafx.h" + +#include "agi/agi.h" +#include "agi/graphics.h" +#include "agi/sprite.h" +#include "agi/text.h" +#include "agi/savegame.h" +#include "agi/keyboard.h" +#include "agi/menu.h" + +namespace Agi { + +#if defined(WIN32) +#define MKDIR(a,b) mkdir(a) +#else +#define MKDIR(a,b) mkdir(a,b) +#endif + +/* Image stack support */ +struct image_stack_element { + uint8 type; + uint8 pad; + int16 parm1; + int16 parm2; + int16 parm3; + int16 parm4; + int16 parm5; + int16 parm6; + int16 parm7; +}; + +#define INITIAL_IMAGE_STACK_SIZE 32 +static int stack_size = 0; +static struct image_stack_element *image_stack = NULL; +static int image_stack_pointer = 0; + +void clear_image_stack(void) { + image_stack_pointer = 0; +} + +void release_image_stack(void) { + if (image_stack) + free(image_stack); + image_stack = NULL; + stack_size = image_stack_pointer = 0; +} + +void record_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3, + int16 p4, int16 p5, int16 p6, int16 p7) { + struct image_stack_element *pnew; + + if (image_stack_pointer == stack_size) { + if (stack_size == 0) { /* first call */ + image_stack = (struct image_stack_element *) + malloc(INITIAL_IMAGE_STACK_SIZE * sizeof(struct image_stack_element)); + stack_size = INITIAL_IMAGE_STACK_SIZE; + } else { /* has to grow */ + struct image_stack_element *new_stack; + new_stack = (struct image_stack_element *) + malloc(2 * stack_size * sizeof(struct image_stack_element)); + memcpy(new_stack, image_stack, stack_size * sizeof(struct image_stack_element)); + free(image_stack); + image_stack = new_stack; + stack_size *= 2; + } + } + + pnew = &image_stack[image_stack_pointer]; + image_stack_pointer++; + + pnew->type = type; + pnew->parm1 = p1; + pnew->parm2 = p2; + pnew->parm3 = p3; + pnew->parm4 = p4; + pnew->parm5 = p5; + pnew->parm6 = p6; + pnew->parm7 = p7; +} + +void replay_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3, + int16 p4, int16 p5, int16 p6, int16 p7) { + switch (type) { + case ADD_PIC: + debugC(8, kDebugLevelMain, "--- decoding picture %d ---", p1); + agi_load_resource(rPICTURE, p1); + decode_picture(p1, p2); + break; + case ADD_VIEW: + agi_load_resource(rVIEW, p1); + add_to_pic(p1, p2, p3, p4, p5, p6, p7); + break; + } +} + +/* */ + +static char *strSig = "AGI:"; + +static void write_uint8(Common::File *f, int8 val) { + f->write(&val, 1); +} + +static void write_sint16(Common::File *f, int16 val) { + static uint8 buf[2]; + buf[0] = (uint8) ((val >> 8) & 255); + buf[1] = (uint8) (val & 255); + f->write(buf, 2); +} + +static void write_uint16(Common::File *f, uint16 val) { + static uint8 buf[2]; + buf[0] = (uint8) ((val >> 8) & 255); + buf[1] = (uint8) (val & 255); + f->write(buf, 2); +} + +static uint8 read_uint8(Common::File *f) { + static uint8 buf[1]; + f->read(buf, 1); + return buf[0]; +} + +static uint16 read_uint16(Common::File *f) { + static uint8 buf[2]; + f->read(buf, 2); + return (buf[0] << 8) | buf[1]; +} + +static int16 read_sint16(Common::File *f) { + static uint8 buf[2]; + f->read(buf, 2); + return (int16) ((buf[0] << 8) | buf[1]); +} + +static void write_string(Common::File *f, char *s) { + write_sint16(f, (int16) strlen(s)); + f->write(s, strlen(s)); +} + +static void read_string(Common::File *f, char *s) { + int16 size = read_sint16(f); + f->read(s, size); + s[size] = (char)0; +} + +static void write_bytes(Common::File *f, char *s, int16 size) { + f->write(s, size); +} + +static void read_bytes(Common::File *f, char *s, int16 size) { + f->read(s, size); +} + +/* + * Version 0: view table has 64 entries + * Version 1: view table has 256 entries (needed in KQ3) + */ +#define SAVEGAME_VERSION 1 + +int save_game(char *s, char *d) { + int16 i; + struct image_stack_element *ptr = image_stack; + Common::File f; + + f.open(s, Common::File::kFileWriteMode); + + if (!f.isOpen()) + return err_BadFileOpen; + + write_bytes(&f, strSig, 8); + write_string(&f, d); + + write_uint8(&f, (uint8) SAVEGAME_VERSION); + write_uint8(&f, (uint8) game.state); + /* game.name */ + write_string(&f, game.id); + /* game.crc */ + + for (i = 0; i < MAX_FLAGS; i++) + write_uint8(&f, game.flags[i]); + for (i = 0; i < MAX_VARS; i++) + write_uint8(&f, game.vars[i]); + + write_sint16(&f, (int8) game.horizon); + write_sint16(&f, (int16) game.line_status); + write_sint16(&f, (int16) game.line_user_input); + write_sint16(&f, (int16) game.line_min_print); + /* game.cursor_pos */ + /* game.input_buffer */ + /* game.echo_buffer */ + /* game.keypress */ + write_sint16(&f, (int16) game.input_mode); + write_sint16(&f, (int16) game.lognum); + + write_sint16(&f, (int16) game.player_control); + write_sint16(&f, (int16) game.quit_prog_now); + write_sint16(&f, (int16) game.status_line); + write_sint16(&f, (int16) game.clock_enabled); + write_sint16(&f, (int16) game.exit_all_logics); + write_sint16(&f, (int16) game.picture_shown); + write_sint16(&f, (int16) game.has_prompt); + write_sint16(&f, (int16) game.game_flags); + + /* Reversed to keep compatibility with old savegames */ + write_sint16(&f, (int16)!game.input_enabled); + + for (i = 0; i < _HEIGHT; i++) + write_uint8(&f, game.pri_table[i]); + + /* game.msg_box_ticks */ + /* game.block */ + /* game.window */ + /* game.has_window */ + + write_sint16(&f, (int16)game.gfx_mode); + write_uint8(&f, game.cursor_char); + write_sint16(&f, (int16)game.color_fg); + write_sint16(&f, (int16)game.color_bg); + + /* game.hires (#ifdef USE_HIRES) */ + /* game.sbuf */ + /* game.ego_words */ + /* game.num_ego_words */ + + write_sint16(&f, (int16)game.num_objects); + for (i = 0; i < (int16)game.num_objects; i++) + write_sint16(&f, (int16)object_get_location(i)); + + /* game.ev_keyp */ + for (i = 0; i < MAX_STRINGS; i++) + write_string(&f, game.strings[i]); + + /* record info about loaded resources */ + for (i = 0; i < MAX_DIRS; i++) { + write_uint8(&f, game.dir_logic[i].flags); + write_sint16(&f, (int16)game.logics[i].sIP); + write_sint16(&f, (int16)game.logics[i].cIP); + } + for (i = 0; i < MAX_DIRS; i++) + write_uint8(&f, game.dir_pic[i].flags); + for (i = 0; i < MAX_DIRS; i++) + write_uint8(&f, game.dir_view[i].flags); + for (i = 0; i < MAX_DIRS; i++) + write_uint8(&f, game.dir_sound[i].flags); + + /* game.pictures */ + /* game.logics */ + /* game.views */ + /* game.sounds */ + + for (i = 0; i < MAX_VIEWTABLE; i++) { + struct vt_entry *v = &game.view_table[i]; + + write_uint8(&f, v->step_time); + write_uint8(&f, v->step_time_count); + write_uint8(&f, v->entry); + write_sint16(&f, v->x_pos); + write_sint16(&f, v->y_pos); + write_uint8(&f, v->current_view); + + /* v->view_data */ + + write_uint8(&f, v->current_loop); + write_uint8(&f, v->num_loops); + + /* v->loop_data */ + + write_uint8(&f, v->current_cel); + write_uint8(&f, v->num_cels); + + /* v->cel_data */ + /* v->cel_data_2 */ + + write_sint16(&f, v->x_pos2); + write_sint16(&f, v->y_pos2); + + /* v->s */ + + write_sint16(&f, v->x_size); + write_sint16(&f, v->y_size); + write_uint8(&f, v->step_size); + write_uint8(&f, v->cycle_time); + write_uint8(&f, v->cycle_time_count); + write_uint8(&f, v->direction); + + write_uint8(&f, v->motion); + write_uint8(&f, v->cycle); + write_uint8(&f, v->priority); + + write_uint16(&f, v->flags); + + write_uint8(&f, v->parm1); + write_uint8(&f, v->parm2); + write_uint8(&f, v->parm3); + write_uint8(&f, v->parm4); + } + + /* Save image stack */ + + for (i = 0; i < image_stack_pointer; i++) { + ptr = &image_stack[i]; + write_uint8(&f, ptr->type); + write_sint16(&f, ptr->parm1); + write_sint16(&f, ptr->parm2); + write_sint16(&f, ptr->parm3); + write_sint16(&f, ptr->parm4); + write_sint16(&f, ptr->parm5); + write_sint16(&f, ptr->parm6); + write_sint16(&f, ptr->parm7); + } + write_uint8(&f, 0); + + f.close(); + + return err_OK; +} + +int load_game(char *s) { + int i, ver, vt_entries = MAX_VIEWTABLE; + uint8 t; + int16 parm[7]; + char sig[8]; + char id[8]; + char description[256]; + Common::File f; + + f.open(s); + + if (!f.isOpen()) + return err_BadFileOpen; + + read_bytes(&f, sig, 8); + if (strncmp(sig, strSig, 8)) { + f.close(); + return err_BadFileOpen; + } + + read_string(&f, description); + + ver = read_uint8(&f); + if (ver == 0) + vt_entries = 64; + game.state = read_uint8(&f); + /* game.name - not saved */ + read_string(&f, id); + if (strcmp(id, game.id)) { + f.close(); + return err_BadFileOpen; + } + /* game.crc - not saved */ + + for (i = 0; i < MAX_FLAGS; i++) + game.flags[i] = read_uint8(&f); + for (i = 0; i < MAX_VARS; i++) + game.vars[i] = read_uint8(&f); + + game.horizon = read_sint16(&f); + game.line_status = read_sint16(&f); + game.line_user_input = read_sint16(&f); + game.line_min_print = read_sint16(&f); + + /* These are never saved */ + game.cursor_pos = 0; + game.input_buffer[0] = 0; + game.echo_buffer[0] = 0; + game.keypress = 0; + + game.input_mode = read_sint16(&f); + game.lognum = read_sint16(&f); + + game.player_control = read_sint16(&f); + game.quit_prog_now = read_sint16(&f); + game.status_line = read_sint16(&f); + game.clock_enabled = read_sint16(&f); + game.exit_all_logics = read_sint16(&f); + game.picture_shown = read_sint16(&f); + game.has_prompt = read_sint16(&f); + game.game_flags = read_sint16(&f); + game.input_enabled = !read_sint16(&f); + + for (i = 0; i < _HEIGHT; i++) + game.pri_table[i] = read_uint8(&f); + + if (game.has_window) + close_window(); + game.msg_box_ticks = 0; + game.block.active = false; + /* game.window - fixed by close_window() */ + /* game.has_window - fixed by close_window() */ + + game.gfx_mode = read_sint16(&f); + game.cursor_char = read_uint8(&f); + game.color_fg = read_sint16(&f); + game.color_bg = read_sint16(&f); + + /* game.hires (#ifdef USE_HIRES) - rebuilt from image stack */ + /* game.sbuf - rebuilt from image stack */ + + /* game.ego_words - fixed by clean_input */ + /* game.num_ego_words - fixed by clean_input */ + + game.num_objects = read_sint16(&f); + for (i = 0; i < (int16) game.num_objects; i++) + object_set_location(i, read_sint16(&f)); + + /* Those are not serialized */ + for (i = 0; i < MAX_DIRS; i++) { + game.ev_keyp[i].occured = false; + } + + for (i = 0; i < MAX_STRINGS; i++) + read_string(&f, game.strings[i]); + + for (i = 0; i < MAX_DIRS; i++) { + if (read_uint8(&f) & RES_LOADED) + agi_load_resource(rLOGIC, i); + else + agi_unload_resource(rLOGIC, i); + game.logics[i].sIP = read_sint16(&f); + game.logics[i].cIP = read_sint16(&f); + } + + for (i = 0; i < MAX_DIRS; i++) { + if (read_uint8(&f) & RES_LOADED) + agi_load_resource(rPICTURE, i); + else + agi_unload_resource(rPICTURE, i); + } + + for (i = 0; i < MAX_DIRS; i++) { + if (read_uint8(&f) & RES_LOADED) + agi_load_resource(rVIEW, i); + else + agi_unload_resource(rVIEW, i); + } + + for (i = 0; i < MAX_DIRS; i++) { + if (read_uint8(&f) & RES_LOADED) + agi_load_resource(rSOUND, i); + else + agi_unload_resource(rSOUND, i); + } + + /* game.pictures - loaded above */ + /* game.logics - loaded above */ + /* game.views - loaded above */ + /* game.sounds - loaded above */ + + for (i = 0; i < vt_entries; i++) { + struct vt_entry *v = &game.view_table[i]; + + v->step_time = read_uint8(&f); + v->step_time_count = read_uint8(&f); + v->entry = read_uint8(&f); + v->x_pos = read_sint16(&f); + v->y_pos = read_sint16(&f); + v->current_view = read_uint8(&f); + + /* v->view_data - fixed below */ + + v->current_loop = read_uint8(&f); + v->num_loops = read_uint8(&f); + + /* v->loop_data - fixed below */ + + v->current_cel = read_uint8(&f); + v->num_cels = read_uint8(&f); + + /* v->cel_data - fixed below */ + /* v->cel_data_2 - fixed below */ + + v->x_pos2 = read_sint16(&f); + v->y_pos2 = read_sint16(&f); + + /* v->s - fixed below */ + + v->x_size = read_sint16(&f); + v->y_size = read_sint16(&f); + v->step_size = read_uint8(&f); + v->cycle_time = read_uint8(&f); + v->cycle_time_count = read_uint8(&f); + v->direction = read_uint8(&f); + + v->motion = read_uint8(&f); + v->cycle = read_uint8(&f); + v->priority = read_uint8(&f); + + v->flags = read_uint16(&f); + + v->parm1 = read_uint8(&f); + v->parm2 = read_uint8(&f); + v->parm3 = read_uint8(&f); + v->parm4 = read_uint8(&f); + } + for (i = vt_entries; i < MAX_VIEWTABLE; i++) { + memset(&game.view_table[i], 0, sizeof(struct vt_entry)); + } + + /* Fix some pointers in viewtable */ + + for (i = 0; i < MAX_VIEWTABLE; i++) { + struct vt_entry *v = &game.view_table[i]; + + if (game.dir_view[v->current_view].offset == _EMPTY) + continue; + + if (!(game.dir_view[v->current_view].flags & RES_LOADED)) + agi_load_resource(rVIEW, v->current_view); + + set_view(v, v->current_view); /* Fix v->view_data */ + set_loop(v, v->current_loop); /* Fix v->loop_data */ + set_cel(v, v->current_cel); /* Fix v->cel_data */ + v->cel_data_2 = v->cel_data; + v->s = NULL; /* not sure if it is used... */ + } + + erase_both(); + + /* Clear input line */ + clear_screen(0); + write_status(); + + /* Recreate background from saved image stack */ + clear_image_stack(); + while ((t = read_uint8(&f)) != 0) { + for (i = 0; i < 7; i++) + parm[i] = read_sint16(&f); + replay_image_stack_call(t, parm[0], parm[1], parm[2], + parm[3], parm[4], parm[5], parm[6]); + } + + f.close(); + + setflag(F_restore_just_ran, true); + + game.has_prompt = 0; /* force input line repaint if necessary */ + clean_input(); + + erase_both(); + blit_both(); + commit_both(); + show_pic(); + do_update(); + + return err_OK; +} + +#define NUM_SLOTS 12 + +static int select_slot() { + int i, key, active = 0; + int rc = -1; + int hm = 2, vm = 3; /* box margins */ + char desc[NUM_SLOTS][40]; + + for (i = 0; i < NUM_SLOTS; i++) { + char name[MAX_PATH]; + Common::File f; + char sig[8]; + sprintf(name, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, i); + f.open(name); + if (!f.isOpen()) { + strcpy(desc[i], " (empty slot)"); + } else { + read_bytes(&f, sig, 8); + if (strncmp(sig, strSig, 8)) { + strcpy(desc[i], "(corrupt file)"); + } else { + read_string(&f, desc[i]); + } + f.close(); + } + } + + while (42) { + char dstr[64]; + for (i = 0; i < NUM_SLOTS; i++) { + sprintf(dstr, "[%-32.32s]", desc[i]); + print_text(dstr, 0, hm + 1, vm + 4 + i, + (40 - 2 * hm) - 1, i == active ? MSG_BOX_COLOUR : MSG_BOX_TEXT, + i == active ? MSG_BOX_TEXT : MSG_BOX_COLOUR); + + } + + poll_timer(); /* msdos driver -> does nothing */ + key = do_poll_keyboard(); + if (!console_keyhandler(key)) { + switch (key) { + case KEY_ENTER: + rc = active; + strncpy(game.strings[MAX_STRINGS], desc[i], MAX_STRINGLEN); + goto press; + case KEY_ESCAPE: + rc = -1; + goto getout; +#ifdef USE_MOUSE + case BUTTON_LEFT: + break; +#endif + case KEY_DOWN: + active++; + active %= NUM_SLOTS; + break; + case KEY_UP: + active--; + if (active < 0) + active = NUM_SLOTS - 1; + break; + } + } + console_cycle(); + } + +press: + debugC(8, kDebugLevelMain | kDebugLevelInput, "Button pressed: %d", rc); + +getout: + close_window(); + return rc; +} + +int savegame_simple() { + char path[MAX_PATH]; + + sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, 0); + save_game(path, "Default savegame"); + + return err_OK; +} + +int savegame_dialog() { + char path[MAX_PATH]; + char *desc; + char *buttons[] = { "Do as I say!", "I regret", NULL }; + char dstr[200]; + int rc, slot = 0; + int hm, vm, hp, vp; /* box margins */ + int w; + + hm = 2; + vm = 3; + hp = hm * CHAR_COLS; + vp = vm * CHAR_LINES; + w = (40 - 2 * hm) - 1; + + sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot); + + draw_window(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp); + print_text("Select a slot in which you wish to save the game:", + 0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOUR); + print_text("Press ENTER to select, ESC cancels", + 0, hm + 1, vm + 17, w, MSG_BOX_TEXT, MSG_BOX_COLOUR); + + slot = select_slot(); + if (slot < 0) /* ESC pressed */ + return err_OK; + + /* Get savegame description */ + draw_window(hp, vp + 5 * CHAR_LINES, GFX_WIDTH - hp, + GFX_HEIGHT - vp - 9 * CHAR_LINES); + print_text("Enter a description for this game:", + 0, hm + 1, vm + 6, w, MSG_BOX_TEXT, MSG_BOX_COLOUR); + draw_rectangle(3 * CHAR_COLS, 11 * CHAR_LINES - 1, + 37 * CHAR_COLS, 12 * CHAR_LINES, MSG_BOX_TEXT); + flush_block(3 * CHAR_COLS, 11 * CHAR_LINES - 1, + 37 * CHAR_COLS, 12 * CHAR_LINES); + + get_string(2, 11, 33, MAX_STRINGS); + print_character(3, 11, game.cursor_char, MSG_BOX_COLOUR, MSG_BOX_TEXT); + do { + main_cycle(); + } while (game.input_mode == INPUT_GETSTRING); + close_window(); + + desc = game.strings[MAX_STRINGS]; + sprintf(dstr, "Are you sure you want to save the game " + "described as:\n\n%s\n\nin slot %d?\n\n\n", desc, slot); + + rc = selection_box(dstr, buttons); + + if (rc != 0) { + message_box("Game NOT saved."); + return err_OK; + } + + sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot); + debugC(8, kDebugLevelMain | kDebugLevelResources, "file is [%s]", path); + + save_game(path, desc); + + message_box("Game saved."); + + return err_OK; +} + +int loadgame_simple() { + char path[MAX_PATH]; + int rc = 0; + + sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, 0); + + erase_both(); + stop_sound(); + close_window(); + + if ((rc = load_game(path)) == err_OK) { + message_box("Game restored."); + game.exit_all_logics = 1; + menu_enable_all(); + } else { + message_box("Error restoring game."); + } + + return rc; +} + +int loadgame_dialog() { + char path[MAX_PATH]; + int rc, slot = 0; + int hm, vm, hp, vp; /* box margins */ + int w; + + hm = 2; + vm = 3; + hp = hm * CHAR_COLS; + vp = vm * CHAR_LINES; + w = (40 - 2 * hm) - 1; + + sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot); + + erase_both(); + stop_sound(); + + draw_window(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp); + print_text("Select a game which you wish to\nrestore:", + 0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOUR); + print_text("Press ENTER to select, ESC cancels", + 0, hm + 1, vm + 17, w, MSG_BOX_TEXT, MSG_BOX_COLOUR); + + slot = select_slot(); + + if (slot < 0) { + message_box("Game NOT restored."); + return err_OK; + } + + sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot); + + if ((rc = load_game(path)) == err_OK) { + message_box("Game restored."); + game.exit_all_logics = 1; + menu_enable_all(); + } else { + message_box("Error restoring game."); + } + + return rc; +} + +} // End of namespace Agi |