void LoadPuz(Puzzle * puz, const std::string & filename, void * /* dummy */) { std::ifstream stream(filename.c_str(), std::ios::in | std::ios::binary); if (stream.fail()) throw FileError(filename); istream_wrapper f(stream); const unsigned short c_primary = f.ReadShort(); if (strcmp(f.ReadString(12).c_str(), "ACROSS&DOWN") != 0) throw FileTypeError("puz"); const unsigned short c_cib = f.ReadShort(); unsigned char c_masked[8]; f.ReadCharArray(c_masked, 8); // Version is "[major].[minor]\0" // We can read puzzles of 1.[anything] std::string versionstr = f.ReadString(4); if (versionstr[0] != '1' || ! isdigit(versionstr[2])) throw LoadError("Unknown puz version."); const unsigned short version = 10 + versionstr[2] - 0x30; f.Skip(2); // 1 unknown short const unsigned short c_grid = f.ReadShort(); f.Skip(2 * 6); // 6 noise shorts const unsigned char width = f.ReadChar(); const unsigned char height = f.ReadChar(); const unsigned short num_clues = f.ReadShort(); const unsigned short grid_type = f.ReadShort(); const unsigned short grid_flag = f.ReadShort(); puz->GetGrid().SetCksum(c_grid); puz->GetGrid().SetType(grid_type); puz->GetGrid().SetFlag(grid_flag); puz->GetGrid().SetSize(width, height); // Read user text and solution std::string solution = f.ReadString(width * height); std::string text = f.ReadString(width * height); // Set the grid's solution and text std::string::iterator sol_it = solution.begin(); std::string::iterator text_it = text.begin(); for (Square * square = puz->GetGrid().First(); square != NULL; square = square->Next()) { // Solution if (*sol_it == '.' || *sol_it == ':' && puz->IsDiagramless()) square->SetSolution(puz::Square::Black); else if (*sol_it == '-') square->SetSolution(puz::Square::Blank); else square->SetSolution(decode_puz(std::string(1, *sol_it))); ++sol_it; // Text if (square->IsBlack() && ! puz->IsDiagramless()) square->SetText(puz::Square::Black); else if (*text_it == '-' || *text_it == 0) square->SetText(puz::Square::Blank); else if (puz->IsDiagramless() && (*text_it == '.' || *text_it == ':')) { // Black squares in a diagramless puzzle. if (*text_it == '.') square->SetText(puz::Square::Black); else if (*text_it == ':') square->SetText(puz::Square::Blank); } else { square->SetText(decode_puz(std::string(1, *text_it))); if (islower(*text_it)) square->AddFlag(FLAG_PENCIL); } ++text_it; } assert(sol_it == solution.end() && text_it == text.end()); puz->NumberGrid(); // General puzzle info puz->SetTitle(decode_puz(f.ReadString())); puz->SetAuthor(decode_puz(f.ReadString())); puz->SetCopyright(decode_puz(f.ReadString())); // Clues std::vector<string_t> clues; clues.reserve(num_clues); // Save unaltered clues for the checksums std::vector<std::string> cksum_clues; cksum_clues.reserve(num_clues); for (size_t i = 0; i < num_clues; ++i) { cksum_clues.push_back(f.ReadString()); clues.push_back(escape_xml(decode_puz(cksum_clues.back()))); } puz->SetAllClues(clues); // Notes std::string notes = f.ReadString(); puz->SetNotes(escape_xml(decode_puz(notes))); puz->SetOk(true); // Try to load the extra sections (i.e. GEXT, LTIM, etc). try { LoadSections(puz, f); } catch (std::ios::failure &) { // EOF here doesn't matter. } // Don't even bother with the checksums, since we check the validity // of the puzzle anyways }
bool Scrambler::ScrambleSolution(unsigned short key_int) { bool ok = m_grid.GetWidth() > 0 && m_grid.GetHeight() > 0; ok = ok && (m_grid.m_flag & FLAG_NO_SOLUTION) == 0; assert(m_grid.First() != NULL); if (! ok) return false; if (key_int == 0) key_int = MakeKey(); assert(1000 <= key_int && key_int <= 9999); // Read the key into an array of single digits unsigned char key[4]; key[0] = int(key_int / 1000) % 10; key[1] = int(key_int / 100) % 10; key[2] = int(key_int / 10) % 10; key[3] = int(key_int / 1) % 10; std::string solution = GetSolutionDown(); unsigned short cksum = Checksummer::cksum_region(solution, 0); if (cksum == 0) return false; size_t length = solution.length(); // Don't scramble really small puzzles if(length < 12) return 0; // Do the scrambling for (int i = 0; i < 4; ++i) { std::string scramble_part; for (size_t j = 0; j < length; ++j) { char letter = solution[j] + key[j % 4]; // Make sure the letter is capital if (letter > 90) letter -= 26; // Range for capital letters assert(isupper(letter)); scramble_part.push_back(letter); } assert(scramble_part.length() == length); scramble_part = ShiftString(scramble_part, key[i]); assert(scramble_part.length() == length); scramble_part = ScrambleString(scramble_part); assert(scramble_part.length() == length); solution = scramble_part; } // Save the scrambled solution to the puzzle file std::string::iterator it = solution.begin(); for (Square * square = m_grid.First(); square != NULL; square = square->Next(DOWN)) { if (square->IsBlack()) continue; // Make sure we preserve any rebus in the solution if (! square->HasSolutionRebus()) square->SetSolution(decode_puz(std::string(1, *it))); else square->SetPlainSolution(*it); ++it; } assert(it == solution.end()); m_grid.m_flag |= FLAG_SCRAMBLED; m_grid.m_cksum = cksum; m_grid.m_key = key_int; return true; }