LibrarianDeprecation = {} local function DeepCopy(originalTable) local copyTable if type(originalTable) == 'table' then copyTable = {} for key, value in pairs(originalTable) do copyTable[key] = DeepCopy(value) end else copyTable = originalTable end return copyTable end local function GetNextBookIndex(bookIndexes) if bookIndexes.bookIndex >= bookIndexes.totalBooks then if bookIndexes.collectionIndex >= bookIndexes.numCollections then if bookIndexes.categoryIndex >= GetNumLoreCategories() then return false else bookIndexes.categoryIndex = bookIndexes.categoryIndex + 1 bookIndexes.numCollections = select(2, GetLoreCategoryInfo(bookIndexes.categoryIndex)) bookIndexes.collectionIndex = 0 end end bookIndexes.collectionIndex = bookIndexes.collectionIndex + 1 bookIndexes.totalBooks = select(4, GetLoreCollectionInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex)) bookIndexes.bookIndex = 0 end bookIndexes.bookIndex = bookIndexes.bookIndex + 1 return true end local function GetBookIdWithTitle(librarian, title) -- list of book that aren't part of the eidetic memory if title == "Adventurers Wanted!" then return 4552 elseif title == "Behold Khunzar-ri's Ambition" then return 5925 elseif title == "Behold Khunzar-ri's Betrayal" then return 5673 elseif title == "Behold Khunzar-ri's Guile" then return 5724 elseif title == "Behold the Lunar Champion" then return 5669 elseif title == "Beldorr's Note" then return 2933 elseif title == "Bounty: Dragons!" then return 5708 elseif title == "Cadwell's Personal Anthem" then return 2517 elseif title == "Crumpled Note" then return 2925 elseif title == "Crumpled Nursery Rhyme" then return 5460 elseif title == "Details on the Midyear Mayhem" then return 4591 elseif title == "Diviner's Journal" then return 4048 elseif title == "Engraved Pedestal" then return 4006 elseif title == "Excavation Orders" then return 4047 elseif title == "Falsehoods and Fallacies of the Eight" then return 1279 elseif title == "Firuth's Writ" then return 4042 elseif title == "From the Exalted Viper" then -- real name is "From the Regent of Fanged Fury" return 2644 elseif title == "The Ghostly Stag" then return 138 elseif title == "Guardian's Decree" then return 4050 elseif title == "The Indrik's Glade" then return 5027 elseif title == "Journal of the King's Seneschal" then return 2015 elseif title == "Journal of Ulrich" then return 132 elseif title == "Journal's Final Pages" then return 2942 elseif title == "Jubilee Cake Voucher" then return 6215 elseif title == "Keeper's Letter" then return 4851 elseif title == "Khasaad's Treasure Map" then return 727 elseif title == "Laughing Moons Ledger" then return 1566 elseif title == "Legend of Shalug the Shark" then return 3285 elseif title == "Letter From Ember" then return 7292 elseif title == "Letter From Isobel" then return 7293 elseif title == "Mezhun's Field Journal" then return 2796 elseif title == "Nerulean's Guide to Phantoms Vol. II" then return 3049 elseif title == "Ode to a Torchbug" then return 2954 elseif title == "On Soul Shriven, vol 3" then return 1551 elseif title == "An Orc Weaponsmith In Murkmire, Part 1" then -- real name is "From Wrothgar to Lilmoth: A Smith's Tale, Vol 1" return 2850 elseif title == "An Orc Weaponsmith In Murkmire, Part 2" then -- real name is "From Wrothgar to Lilmoth: A Smith's Tale, Vol 2" return 2851 elseif title == "An Orc Weaponsmith In Murkmire, Part 3" then -- real name is "From Wrothgar to Lilmoth: A Smith's Tale, Vol 3" return 2852 elseif title == "Orcthane's Orders" then return 439 elseif title == "Plea to Maximinus" then return 2659 elseif title == "PRISONER: CLARISSE LAURENT" then return 2908 elseif title == "PRISONER: RAYNOR VANOS" then return 2907 elseif title == "PRISONER: TELENGER" then return 2909 elseif title == "Report on Dominion Activities" then return 147 elseif title == "Royal Messenger's Fate" then return 2701 elseif title == "Seeking New Members!" then return 7168 elseif title == "Six Are the Walking Ways" then return 4445 elseif title == "The Song of the Word" then return 4446 elseif title == "Southern Elsweyr Needs You!" then return 5697 elseif title == "Statue of Amminus Entius" then return 3419 elseif title == "Statue of Cavor Merula" then return 3416 elseif title == "Statue of Justia Desticus" then return 3417 elseif title == "Statue of Rusio Olo" then return 3418 elseif title == "Terran's Notes" then return 3047 elseif title == "The Tomb of Ja'darri" then return 5737 elseif title == "Torag ag Krazak, Uz" then return 3089 elseif title == "Tribute Challengers - Novice Tournament" then return 7169 elseif title == "Ushutha's Journal" then return 3162 elseif title == "The Year 2920, Vol. 28" then return 2945 elseif title == "Shrine of Mother Morrowind" then return 4495 elseif title == "My Dearest Love" then return 4115 elseif title == "Crafting Motif 69: Dead-Water Chest Pieces" then return 5335 elseif title == "Crafting Motif 16: Glass Style" then return 3174 elseif title == "Bravil, Part 1" then return 792 elseif title == "Tharayyas' Journal, Entry 10" then return 1399 elseif title == "The True Nature of Orcs (Banned Ed.)" then return 13 elseif title == "Guild Memo on Soul Trapping" then return 240 elseif title == "Crafting Motif 15: Dwemer Chests" then -- Crafting Motif 15: Dwemer Chests Pieces return 2861 elseif title == "Crafting Motif 15: Dwemer" then return 2856 elseif title == "Crafting Motif 16: Glass Style" then return 3174 elseif title == "Crafting Motif 37: Ebony Style" then return 3592 elseif title == "Crafting Motif 51: Redoran Axes" then -- Crafting Motif 52 return 4649 elseif title == "Crafting Motif 51: Redoran Belts" then return 4650 elseif title == "Crafting Motif 51: Redoran Boots" then return 4651 elseif title == "Crafting Motif 51: Redoran Bows" then return 4652 elseif title == "Crafting Motif 51: Redoran Chests" then return 4653 elseif title == "Crafting Motif 51: Redoran Daggers" then return 4654 elseif title == "Crafting Motif 51: Redoran Gloves" then return 4655 elseif title == "Crafting Motif 51: Redoran Helmets" then return 4656 elseif title == "Crafting Motif 51: Redoran Legs" then return 4657 elseif title == "Crafting Motif 51: Redoran Maces" then return 4658 elseif title == "Crafting Motif 51: Redoran Shields" then return 4659 elseif title == "Crafting Motif 51: Redoran Shoulders" then return 4660 elseif title == "Crafting Motif 51: Redoran Staves" then return 4661 elseif title == "Crafting Motif 51: Redoran Swords" then return 4662 elseif title == "Crafting Motif 52: Hlaalu Axes" then -- Crafting Motif 51 return 4633 elseif title == "Crafting Motif 52: Hlaalu Belts" then return 4634 elseif title == "Crafting Motif 52: Hlaalu Boots" then return 4635 elseif title == "Crafting Motif 52: Hlaalu Bows" then return 4636 elseif title == "Crafting Motif 52: Hlaalu Chests" then return 4637 elseif title == "Crafting Motif 52: Hlaalu Daggers" then return 4638 elseif title == "Crafting Motif 52: Hlaalu Gloves" then return 4639 elseif title == "Crafting Motif 52: Hlaalu Helmets" then return 4640 elseif title == "Crafting Motif 52: Hlaalu Legs" then return 4641 elseif title == "Crafting Motif 52: Hlaalu Maces" then return 4642 elseif title == "Crafting Motif 52: Hlaalu Shields" then return 4643 elseif title == "Crafting Motif 52: Hlaalu Shoulders" then return 4644 elseif title == "Crafting Motif 52: Hlaalu Staves" then return 4645 elseif title == "Crafting Motif 52: Hlaalu Swords" then return 4646 elseif title == "Crafting Motif 56: Apostle Chest Pieces" then return 4748 elseif title == "Crafting Motif 56: Apostle Shoulder Armor" then return 4755 elseif title == "Crafting Motif 56: Ebonshadow Axes" then -- Crafting Motif 57 return 4774 elseif title == "Crafting Motif 56: Ebonshadow Belts" then return 4775 elseif title == "Crafting Motif 56: Ebonshadow Boots" then return 4776 elseif title == "Crafting Motif 56: Ebonshadow Bows" then return 4777 elseif title == "Crafting Motif 56: Ebonshadow Chests" then return 4778 elseif title == "Crafting Motif 56: Ebonshadow Daggers" then return 4779 elseif title == "Crafting Motif 56: Ebonshadow Gloves" then return 4780 elseif title == "Crafting Motif 56: Ebonshadow Helmets" then return 4781 elseif title == "Crafting Motif 56: Ebonshadow Leg Greaves" then return 4782 elseif title == "Crafting Motif 56: Ebonshadow Maces" then return 4783 elseif title == "Crafting Motif 56: Ebonshadow Shields" then return 4784 elseif title == "Crafting Motif 56: Ebonshadow Shoulders" then return 4785 elseif title == "Crafting Motif 56: Ebonshadow Staves" then return 4786 elseif title == "Crafting Motif 56: Ebonshadow Swords" then return 4787 end if not librarian.globalSavedVars.failedDeprecation then librarian.globalSavedVars.failedDeprecation = { librarianBookId = 100000, bookList = {} } end for _, book in ipairs(librarian.globalSavedVars.failedDeprecation.bookList) do if book.title == title then return book.bookId end end local nextBookId = librarian.globalSavedVars.failedDeprecation.librarianBookId local newFailedDeprecation = { title = title, bookId = nextBookId } table.insert(librarian.globalSavedVars.failedDeprecation.bookList, newFailedDeprecation) librarian.globalSavedVars.failedDeprecation.librarianBookId = nextBookId + 1 return nextBookId end function LibrarianDeprecation:UpdateSavedVariables(librarian) -- before version 3.0 there was no saveVersion -- In this version, the bookId is now used as identifier instead of the title (because several book have the same title) if not librarian.globalSavedVars.saveVersion then librarian.globalSavedVars.saveVersion = 2 -- let's copy the current save into another file just in case something wrong happen during deprecation local backupGlobalSavedVars = ZO_SavedVars:NewAccountWide("LibrarianDeprecation_SavedVariables_Backup", 1, nil, {}, nil) backupGlobalSavedVars.books = DeepCopy(librarian.books) -- if this player doesn't know any book yet, no need to go further if next(librarian.books) ~= nil then local GetLoreBookInfo = GetLoreBookInfo local bookIndexes = { categoryIndex = 0, collectionIndex = 0, bookIndex = 0, numCollections = 0, totalBooks = 0, } while true do if not GetNextBookIndex(bookIndexes) then break end local title, _, _, bookId = GetLoreBookInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex, bookIndexes.bookIndex) local book = nil for _,bookIt in pairs(librarian.books) do if bookIt.title == title then book = bookIt break end end if book then if not book.bookId then book.bookId = bookId else -- there is a collision (2 books with same name) so we add an entry for the new one local newBook = { bookId = bookId, title = title } librarian:AddBookToGlobalSave(newBook) newBook.timeStamp = book.timeStamp end end end -- now we can erase all the field that are useless (ESO API is performant enough to get these data only when needed) -- it will also help keeping the save small and avoid issues with corrupted save for _,book in pairs(librarian.books) do if book.bookId then book.title = nil book.body = nil book.medium = nil book.showTitle = nil else book.bookId = GetBookIdWithTitle(librarian, book.title) local categoryIndex, collectionIndex, bookIndex = GetLoreBookIndicesFromBookId(book.bookId) if categoryIndex and collectionIndex and bookIndex then -- the deprecation found a valid book, so we can erase the content of this one as well book.title = nil book.body = nil book.medium = nil book.showTitle = nil end end book.wordCount = nil -- it will be computed when building the list (it will allow to switch language and this will still be acurate) end end elseif librarian.globalSavedVars.saveVersion == 1 then librarian.globalSavedVars.saveVersion = 2 -- copy the backup save which was inside the Librarian file for version 1 in LibrarianDeprecation file local previousBackupGlobalSavedVars = ZO_SavedVars:NewAccountWide("Librarian_SavedVariables_Backup", 1, nil, {}, nil) if previousBackupGlobalSavedVars.books then local newBackupGlobalSavedVars = ZO_SavedVars:NewAccountWide("LibrarianDeprecation_SavedVariables_Backup", 1, nil, {}, nil) newBackupGlobalSavedVars.books = previousBackupGlobalSavedVars.books previousBackupGlobalSavedVars.books = nil end end if not librarian.localSavedVars.saveVersion then librarian.localSavedVars.saveVersion = 2 -- let's copy the current save into another file just in case something wrong happen during deprecation local backupLocalSavedVars = ZO_SavedVars:New("LibrarianDeprecation_SavedVariables_Backup", 1, nil, {}, nil) backupLocalSavedVars.characterBooks = DeepCopy(librarian.characterBooks) local GetLoreBookInfo = GetLoreBookInfo local bookIndexes = { categoryIndex = 0, collectionIndex = 0, bookIndex = 0, numCollections = 0, totalBooks = 0, } while true do if not GetNextBookIndex(bookIndexes) then break end local title, _, known, bookId = GetLoreBookInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex, bookIndexes.bookIndex) if known then local characterBook = nil for _,characterBookIt in ipairs(librarian.characterBooks) do if characterBookIt.title == title then characterBook = characterBookIt break end end if characterBook then if not characterBook.bookId then characterBook.bookId = bookId else local newCharacterBook = { title = title, timeStamp = characterBook.timeStamp, bookId = bookId } table.insert(librarian.characterBooks, newCharacterBook) end end end end -- now we can remove the title from the characterBooks and rely only on the bookId (it saves space) for _,characterBook in ipairs(librarian.characterBooks) do if characterBook.bookId then characterBook.title = nil else characterBook.bookId = GetBookIdWithTitle(librarian, characterBook.title) local categoryIndex, collectionIndex, bookIndex = GetLoreBookIndicesFromBookId(characterBook.bookId) if categoryIndex and collectionIndex and bookIndex then -- the deprecation found a valid book, so we can erase the content of this one as well characterBook.title = nil end end end elseif librarian.localSavedVars.saveVersion == 1 then librarian.localSavedVars.saveVersion = 2 -- copy the backup save which was inside the Librarian file for version 1 in LibrarianDeprecation file local previousBackupLocalSavedVars = ZO_SavedVars:New("Librarian_SavedVariables_Backup", 1, nil, {}, nil) if previousBackupLocalSavedVars.characterBooks then local newBackupLocalSavedVars = ZO_SavedVars:New("LibrarianDeprecation_SavedVariables_Backup", 1, nil, {}, nil) newBackupLocalSavedVars.characterBooks = previousBackupLocalSavedVars.characterBooks previousBackupLocalSavedVars.characterBooks = nil end end end