diff --git a/internal/core/core.go b/internal/core/core.go index 10cc0b2..cccd917 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -3,7 +3,11 @@ package core import ( "archive/zip" "bytes" + "encoding/json" + "fmt" "io" + "net/http" + "net/url" "os" "path/filepath" @@ -11,14 +15,14 @@ import ( "github.com/pkg/errors" ) -func extractRadarImage(f *zip.File) error { +func extractRadarImage(f *zip.File, targetDir string) error { r, err := f.Open() if err != nil { return errors.Wrapf(err, "failed to open DDS file %q from archive", f.Name) } defer r.Close() - ddsFileName := filepath.Base(f.Name) + ddsFileName := filepath.Join(targetDir, filepath.Base(f.Name)) ddsF, err := os.Create(ddsFileName) if err != nil { return errors.Wrapf(err, "failed to create DDS file %q on disk", ddsFileName) @@ -33,7 +37,12 @@ func extractRadarImage(f *zip.File) error { return nil } -func ExtractRadarImages(f *bsp.Bsp) error { +func ExtractRadarImages(f *bsp.Bsp, targetDir string) error { + err := os.MkdirAll(targetDir, 0744) + if err != nil { + return errors.Wrapf(err, "failed to create target dir %q", targetDir) + } + b := f.RawLump(bsp.LumpPakfile).RawContents() r := bytes.NewReader(b) zipR, err := zip.NewReader(r, int64(len(b))) @@ -43,7 +52,7 @@ func ExtractRadarImages(f *bsp.Bsp) error { for _, pakF := range zipR.File { if filepath.Ext(pakF.Name) == ".dds" { - err := extractRadarImage(pakF) + err := extractRadarImage(pakF, targetDir) if err != nil { return errors.Wrapf(err, "failed to extract radar image %q", pakF.Name) } @@ -52,3 +61,78 @@ func ExtractRadarImages(f *bsp.Bsp) error { return nil } + +type GetPublishedFileDetailsResponse struct { + Response struct { + Result int `json:"result"` + Resultcount int `json:"resultcount"` + Publishedfiledetails []struct { + Publishedfileid string `json:"publishedfileid"` + Result int `json:"result"` + Creator string `json:"creator"` + CreatorAppID int `json:"creator_app_id"` + ConsumerAppID int `json:"consumer_app_id"` + Filename string `json:"filename"` + FileSize int `json:"file_size"` + FileURL string `json:"file_url"` + HcontentFile string `json:"hcontent_file"` + PreviewURL string `json:"preview_url"` + HcontentPreview string `json:"hcontent_preview"` + Title string `json:"title"` + Description string `json:"description"` + TimeCreated int `json:"time_created"` + TimeUpdated int `json:"time_updated"` + Visibility int `json:"visibility"` + Banned int `json:"banned"` + BanReason string `json:"ban_reason"` + Subscriptions int `json:"subscriptions"` + Favorited int `json:"favorited"` + LifetimeSubscriptions int `json:"lifetime_subscriptions"` + LifetimeFavorited int `json:"lifetime_favorited"` + Views int `json:"views"` + Tags []struct { + Tag string `json:"tag"` + } `json:"tags"` + } `json:"publishedfiledetails"` + } `json:"response"` +} + +func GetWorkshopFileDetails(workshopID int) (GetPublishedFileDetailsResponse, error) { + payload := url.Values{ + "itemcount": []string{"1"}, + "publishedfileids[0]": []string{fmt.Sprint(workshopID)}, + } + + resp, err := http.PostForm("http://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1", payload) + if err != nil { + return GetPublishedFileDetailsResponse{}, err + } + + defer resp.Body.Close() + + var respData GetPublishedFileDetailsResponse + dec := json.NewDecoder(resp.Body) + err = dec.Decode(&respData) + if err != nil { + return GetPublishedFileDetailsResponse{}, err + } + + return respData, nil +} + +func DownloadWorkshopItem(workshopID int, w io.Writer) error { + details, err := GetWorkshopFileDetails(workshopID) + if err != nil { + return err + } + + resp, err := http.Get(details.Response.Publishedfiledetails[0].FileURL) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = io.Copy(w, resp.Body) + + return err +} diff --git a/internal/core/core_test.go b/internal/core/core_test.go index cc65715..16b0823 100644 --- a/internal/core/core_test.go +++ b/internal/core/core_test.go @@ -1,6 +1,12 @@ package core_test import ( + "archive/zip" + "bytes" + "encoding/json" + "fmt" + "os" + "path/filepath" "testing" "github.com/galaco/bsp" @@ -14,6 +20,72 @@ func TestCore(t *testing.T) { f, err := bsp.ReadFromFile(bspFilePath) assert.NoErrorf(t, err, "failed to open BSP file %q", bspFilePath) - err = core.ExtractRadarImages(f) + err = core.ExtractRadarImages(f, filepath.Join(os.TempDir(), "radar-overviews")) assert.NoError(t, err, "failed to extract radar images from BSP file %q", bspFilePath) } + +type crcTable struct { + Maps []struct { + Name string `json:"map_name"` + Crc uint32 `json:"map_crc"` + WorkshopID int `json:"workshop_id"` + } `json:"maps"` +} + +func TestDownload(t *testing.T) { + const crcTablePath = "../../test/data/crc_table.json" + + f, err := os.Open(crcTablePath) + assert.NoErrorf(t, err, "failed to open CRC table file %q", crcTablePath) + defer f.Close() + + var tab crcTable + dec := json.NewDecoder(f) + err = dec.Decode(&tab) + assert.NoErrorf(t, err, "failed to decode CRC table %q as JSON", crcTablePath) + + fDownload, err := os.Create(filepath.Join(os.TempDir(), fmt.Sprintf("%s.bsp.zip", tab.Maps[0].Name))) + assert.NoErrorf(t, err, "failed to create target file for download %q", fDownload.Name()) + defer f.Close() + + err = core.DownloadWorkshopItem(tab.Maps[0].WorkshopID, fDownload) + assert.NoErrorf(t, err, "failed to download workshop item %q", tab.Maps[0].WorkshopID) +} + +func TestE2E(t *testing.T) { + const crcTablePath = "../../test/data/crc_table.json" + + f, err := os.Open(crcTablePath) + assert.NoErrorf(t, err, "failed to open CRC table file %q", crcTablePath) + defer f.Close() + + var tab crcTable + dec := json.NewDecoder(f) + err = dec.Decode(&tab) + assert.NoErrorf(t, err, "failed to decode CRC table %q as JSON", crcTablePath) + + var buf bytes.Buffer + + workshopID := tab.Maps[0].WorkshopID + err = core.DownloadWorkshopItem(workshopID, &buf) + assert.NoErrorf(t, err, "failed to download workshop item %q", workshopID) + + b := buf.Bytes() + r := bytes.NewReader(b) + + zipR, err := zip.NewReader(r, int64(len(b))) + assert.NoErrorf(t, err, "failed to open zip reader for workshop file with ID %q", workshopID) + + for _, zipF := range zipR.File { + if filepath.Ext(zipF.Name) == ".bsp" { + bspR, err := zipF.Open() + assert.NoErrorf(t, err, "failed to open BSP file in zip %q", zipF.Name) + + bsp, err := bsp.ReadFromStream(bspR) + assert.NoErrorf(t, err, "failed to read BSP data from zip file stream %q", zipF.Name) + + err = core.ExtractRadarImages(bsp, filepath.Join(os.TempDir(), "radar-overviews")) + assert.NoErrorf(t, err, "failed to extract radar images from BSP data of file %q", zipF.Name) + } + } +} diff --git a/test/data/crc_table.json b/test/data/crc_table.json new file mode 100644 index 0000000..8b1c94c --- /dev/null +++ b/test/data/crc_table.json @@ -0,0 +1 @@ +{"maps":[{"map_crc":3124679106,"workshop_id":157233767,"map_name":"de_nuke"},{"map_crc":1182019033,"workshop_id":157435589,"map_name":"de_dust2"},{"map_crc":2716864783,"workshop_id":157435963,"map_name":"de_inferno"},{"map_crc":1990670420,"workshop_id":171506969,"map_name":"de_dust2"},{"map_crc":3593104352,"workshop_id":171506969,"map_name":"de_dust2"},{"map_crc":1265480659,"workshop_id":171508198,"map_name":"de_aztec"},{"map_crc":2169684774,"workshop_id":171510884,"map_name":"de_train"},{"map_crc":3407270300,"workshop_id":171510884,"map_name":"de_train"},{"map_crc":611172969,"workshop_id":171511992,"map_name":"de_inferno"},{"map_crc":24080782,"workshop_id":179626579,"map_name":"de_inferno"},{"map_crc":1657346892,"workshop_id":171512316,"map_name":"de_nuke"},{"map_crc":2931036375,"workshop_id":171512732,"map_name":"de_mirage"},{"map_crc":3163471837,"workshop_id":179626216,"map_name":"de_mirage"},{"map_crc":2721087398,"workshop_id":192698831,"map_name":"de_mirage"},{"map_crc":3917140838,"workshop_id":195000763,"map_name":"de_mirage"},{"map_crc":495851354,"workshop_id":195001140,"map_name":"de_inferno"},{"map_crc":2182392817,"workshop_id":213863953,"map_name":"de_overpass"},{"map_crc":681463358,"workshop_id":213864557,"map_name":"de_cbble"},{"map_crc":469854806,"workshop_id":222987850,"map_name":"cs_assault"},{"map_crc":954507423,"workshop_id":222997267,"map_name":"de_inferno"},{"map_crc":4181170190,"workshop_id":222996490,"map_name":"de_mirage"},{"map_crc":1773019424,"workshop_id":222992981,"map_name":"de_dust2"},{"map_crc":1426511170,"workshop_id":222995558,"map_name":"de_overpass"},{"map_crc":1180058324,"workshop_id":227300574,"map_name":"de_overpass"},{"map_crc":3282979055,"workshop_id":229914070,"map_name":"de_overpass"},{"map_crc":2216389884,"workshop_id":229977455,"map_name":"de_seaside"},{"map_crc":2222825797,"workshop_id":229976401,"map_name":"de_ali"},{"map_crc":1184087776,"workshop_id":229979813,"map_name":"cs_thunder"},{"map_crc":2381068684,"workshop_id":229980329,"map_name":"cs_motel"},{"map_crc":4106514274,"workshop_id":230304416,"map_name":"de_nuke"},{"map_crc":1712786004,"workshop_id":232196332,"map_name":"de_inferno"},{"map_crc":3479754609,"workshop_id":232198948,"map_name":"cs_downtown"},{"map_crc":3869324569,"workshop_id":232199412,"map_name":"de_favela"},{"map_crc":2304800004,"workshop_id":234740424,"map_name":"de_dust2"},{"map_crc":3204641948,"workshop_id":234742083,"map_name":"de_mirage"},{"map_crc":2800198781,"workshop_id":234742531,"map_name":"de_overpass"},{"map_crc":1098955007,"workshop_id":242433788,"map_name":"de_dust2"},{"map_crc":1507727744,"workshop_id":242434336,"map_name":"de_inferno"},{"map_crc":243676019,"workshop_id":242434829,"map_name":"de_overpass"},{"map_crc":67396432,"workshop_id":252076346,"map_name":"de_mirage"},{"map_crc":1302144222,"workshop_id":252117838,"map_name":"de_vertigo"},{"map_crc":3147083090,"workshop_id":254873696,"map_name":"de_seaside"},{"map_crc":3829199489,"workshop_id":254961518,"map_name":"de_overpass"},{"map_crc":2230463619,"workshop_id":259816283,"map_name":"cs_agency"},{"map_crc":2050564121,"workshop_id":264632065,"map_name":"de_overpass"},{"map_crc":699166707,"workshop_id":264631731,"map_name":"de_cbble"},{"map_crc":178363298,"workshop_id":275063281,"map_name":"de_inferno"},{"map_crc":1693914067,"workshop_id":275063899,"map_name":"de_nuke"},{"map_crc":3861756306,"workshop_id":275064620,"map_name":"de_mirage"},{"map_crc":1333465166,"workshop_id":276172833,"map_name":"de_dust2"},{"map_crc":2667616958,"workshop_id":283680274,"map_name":"de_inferno"},{"map_crc":4131726749,"workshop_id":283681498,"map_name":"de_dust2"},{"map_crc":71263663,"workshop_id":283682300,"map_name":"de_dust"},{"map_crc":2361832512,"workshop_id":283683418,"map_name":"de_mirage"},{"map_crc":2584876302,"workshop_id":283684475,"map_name":"cs_insertion"},{"map_crc":1467627813,"workshop_id":283700717,"map_name":"de_overgrown"},{"map_crc":1642975600,"workshop_id":284121354,"map_name":"de_castle"},{"map_crc":2206382053,"workshop_id":287082924,"map_name":"de_blackgold"},{"map_crc":872884925,"workshop_id":287083818,"map_name":"de_overpass"},{"map_crc":2505523746,"workshop_id":287084251,"map_name":"de_cbble"},{"map_crc":680519868,"workshop_id":287084679,"map_name":"de_mirage"},{"map_crc":510127532,"workshop_id":287628840,"map_name":"de_overpass"},{"map_crc":528621906,"workshop_id":290373417,"map_name":"cs_insertion"},{"map_crc":2665301992,"workshop_id":290374107,"map_name":"de_cache"},{"map_crc":3339373690,"workshop_id":293589303,"map_name":"de_mist"},{"map_crc":2726960029,"workshop_id":293679891,"map_name":"de_cache"},{"map_crc":574035734,"workshop_id":302337189,"map_name":"cs_insertion"},{"map_crc":2227655590,"workshop_id":302337893,"map_name":"cs_rush"},{"map_crc":2778284110,"workshop_id":306598579,"map_name":"de_mirage"},{"map_crc":3750908943,"workshop_id":306599111,"map_name":"de_overpass"},{"map_crc":350409915,"workshop_id":309282223,"map_name":"de_dust2"},{"map_crc":1268235177,"workshop_id":309282817,"map_name":"de_cbble"},{"map_crc":437687737,"workshop_id":312370920,"map_name":"cs_insertion"},{"map_crc":746974603,"workshop_id":312371306,"map_name":"de_mist"},{"map_crc":1471988799,"workshop_id":315316316,"map_name":"de_inferno"},{"map_crc":448620192,"workshop_id":320742778,"map_name":"de_overpass"},{"map_crc":207856091,"workshop_id":330307812,"map_name":"de_mirage"},{"map_crc":2914838996,"workshop_id":330308185,"map_name":"de_inferno"},{"map_crc":1110353880,"workshop_id":339957619,"map_name":"de_dust"},{"map_crc":2313477443,"workshop_id":339958561,"map_name":"de_lake"},{"map_crc":840820667,"workshop_id":339959168,"map_name":"de_stmarc"},{"map_crc":1430740036,"workshop_id":339960492,"map_name":"de_safehouse"},{"map_crc":2420599138,"workshop_id":340860767,"map_name":"cs_backalley"},{"map_crc":3807715396,"workshop_id":340898532,"map_name":"de_facade"},{"map_crc":4077622050,"workshop_id":344055221,"map_name":"cs_workout"},{"map_crc":25996201,"workshop_id":344056007,"map_name":"de_bazaar"},{"map_crc":3435910366,"workshop_id":344057229,"map_name":"de_facade"},{"map_crc":1944765357,"workshop_id":344058447,"map_name":"de_season"},{"map_crc":3689710066,"workshop_id":344059455,"map_name":"de_marquis"},{"map_crc":3048010577,"workshop_id":344920951,"map_name":"de_bazaar"},{"map_crc":798364328,"workshop_id":350662766,"map_name":"cs_backalley"},{"map_crc":2022990971,"workshop_id":350668332,"map_name":"de_season"},{"map_crc":3842996249,"workshop_id":350668981,"map_name":"de_facade"},{"map_crc":3890022757,"workshop_id":350669476,"map_name":"de_overpass"},{"map_crc":1348378220,"workshop_id":350670788,"map_name":"de_nuke"},{"map_crc":3303850584,"workshop_id":353839326,"map_name":"de_train"},{"map_crc":2931701491,"workshop_id":354645531,"map_name":"de_train"},{"map_crc":1506490723,"workshop_id":356784023,"map_name":"de_mirage"},{"map_crc":2385925410,"workshop_id":358032706,"map_name":"de_train"},{"map_crc":1181631038,"workshop_id":371240803,"map_name":"de_train"},{"map_crc":2020322063,"workshop_id":371241418,"map_name":"de_mirage"},{"map_crc":2783019610,"workshop_id":371241883,"map_name":"de_dust2"},{"map_crc":1284780985,"workshop_id":371242549,"map_name":"de_facade"},{"map_crc":977028606,"workshop_id":375144790,"map_name":"de_cache"},{"map_crc":3617927153,"workshop_id":375219732,"map_name":"de_cbble"},{"map_crc":3791254604,"workshop_id":383036485,"map_name":"de_mirage"},{"map_crc":3453760139,"workshop_id":383037025,"map_name":"de_nuke"},{"map_crc":2144084887,"workshop_id":383037414,"map_name":"de_overpass"},{"map_crc":2219429786,"workshop_id":386577619,"map_name":"de_cbble"},{"map_crc":1850018851,"workshop_id":390492368,"map_name":"de_cbble"},{"map_crc":2346717472,"workshop_id":395001723,"map_name":"de_cache"},{"map_crc":3383606783,"workshop_id":395002275,"map_name":"de_season"},{"map_crc":3693561685,"workshop_id":395041395,"map_name":"de_cbble"},{"map_crc":1776147095,"workshop_id":416882150,"map_name":"de_cbble"},{"map_crc":2907050439,"workshop_id":416882725,"map_name":"de_dust2"},{"map_crc":3639401489,"workshop_id":416883208,"map_name":"de_mirage"},{"map_crc":2958343363,"workshop_id":416883653,"map_name":"de_train"},{"map_crc":2533041239,"workshop_id":416884017,"map_name":"de_overpass"},{"map_crc":3764900504,"workshop_id":416884493,"map_name":"de_shorttrain"},{"map_crc":2610742889,"workshop_id":416885049,"map_name":"de_cache"},{"map_crc":3170284390,"workshop_id":449826996,"map_name":"de_cache"},{"map_crc":1041078129,"workshop_id":449827753,"map_name":"de_season"},{"map_crc":3232121040,"workshop_id":449828270,"map_name":"de_mirage"},{"map_crc":3416184308,"workshop_id":450883414,"map_name":"de_log"},{"map_crc":1523841814,"workshop_id":451447326,"map_name":"de_rails"},{"map_crc":145299558,"workshop_id":454705526,"map_name":"de_log"},{"map_crc":3409813591,"workshop_id":458344419,"map_name":"de_rails"},{"map_crc":65011809,"workshop_id":463255961,"map_name":"de_zoo"},{"map_crc":3488980994,"workshop_id":477840228,"map_name":"de_dust2"},{"map_crc":2603599403,"workshop_id":477840852,"map_name":"de_overpass"},{"map_crc":2136137933,"workshop_id":511108634,"map_name":"de_dust2"},{"map_crc":4173578470,"workshop_id":511109129,"map_name":"de_train"},{"map_crc":2662578373,"workshop_id":511115741,"map_name":"de_cache"},{"map_crc":2789023136,"workshop_id":519412983,"map_name":"de_cache"},{"map_crc":3696616614,"workshop_id":531432592,"map_name":"de_cbble"},{"map_crc":3335676469,"workshop_id":534662342,"map_name":"de_mirage"},{"map_crc":1674089145,"workshop_id":534663598,"map_name":"de_overpass"},{"map_crc":2882970637,"workshop_id":538091991,"map_name":"de_train"},{"map_crc":2521539352,"workshop_id":539273673,"map_name":"de_cache"},{"map_crc":3601621302,"workshop_id":542337148,"map_name":"de_cache"},{"map_crc":597569438,"workshop_id":626511905,"map_name":"de_cache"},{"map_crc":4019143237,"workshop_id":626513993,"map_name":"de_nuke"},{"map_crc":1450024005,"workshop_id":626513402,"map_name":"de_mirage"},{"map_crc":3437636129,"workshop_id":627908695,"map_name":"de_cache"},{"map_crc":3637489157,"workshop_id":627910098,"map_name":"de_santorini"},{"map_crc":119719789,"workshop_id":627911252,"map_name":"de_nuke"},{"map_crc":1509996163,"workshop_id":627912263,"map_name":"de_mikla"},{"map_crc":4212310210,"workshop_id":632485493,"map_name":"de_coast"},{"map_crc":3239902476,"workshop_id":632486228,"map_name":"de_mikla"},{"map_crc":3960897596,"workshop_id":632484325,"map_name":"de_santorini"},{"map_crc":3443643978,"workshop_id":632483369,"map_name":"de_cache"},{"map_crc":1122516582,"workshop_id":632486961,"map_name":"de_cruise"},{"map_crc":1439747482,"workshop_id":632487767,"map_name":"de_royal"},{"map_crc":2925187832,"workshop_id":636243078,"map_name":"de_nuke"},{"map_crc":3333955648,"workshop_id":646588823,"map_name":"de_nuke"},{"map_crc":3965900419,"workshop_id":650679794,"map_name":"de_overpass"},{"map_crc":633591897,"workshop_id":669564161,"map_name":"de_tulip"},{"map_crc":2966862683,"workshop_id":669564729,"map_name":"de_mirage"},{"map_crc":1323791646,"workshop_id":678529248,"map_name":"de_cache"},{"map_crc":1689043877,"workshop_id":678613618,"map_name":"de_nuke"},{"map_crc":2515589719,"workshop_id":686474286,"map_name":"de_cache"},{"map_crc":211399940,"workshop_id":698753657,"map_name":"de_cbble"},{"map_crc":2494823213,"workshop_id":698754311,"map_name":"de_mirage"},{"map_crc":1696063123,"workshop_id":698755421,"map_name":"de_nuke"},{"map_crc":2087624451,"workshop_id":699412941,"map_name":"de_cache"},{"map_crc":152934076,"workshop_id":723992424,"map_name":"de_cache"},{"map_crc":4167847735,"workshop_id":759223419,"map_name":"de_cache"},{"map_crc":3454318515,"workshop_id":778803540,"map_name":"de_inferno"},{"map_crc":1122516582,"workshop_id":779308707,"map_name":"cs_cruise"},{"map_crc":623924849,"workshop_id":779309165,"map_name":"cs_agency"},{"map_crc":1667918186,"workshop_id":779309420,"map_name":"cs_backalley"},{"map_crc":3082674078,"workshop_id":779309846,"map_name":"cs_workout"},{"map_crc":2413541584,"workshop_id":779310239,"map_name":"cs_insertion"},{"map_crc":208800322,"workshop_id":779310571,"map_name":"cs_rush"},{"map_crc":671505709,"workshop_id":779310891,"map_name":"cs_motel"},{"map_crc":2314858465,"workshop_id":779343723,"map_name":"cs_thunder"},{"map_crc":2054675660,"workshop_id":779344154,"map_name":"cs_downtown"},{"map_crc":200063142,"workshop_id":779294699,"map_name":"de_bazaar"},{"map_crc":4117170336,"workshop_id":779295144,"map_name":"de_facade"},{"map_crc":3976762865,"workshop_id":779295548,"map_name":"de_marquis"},{"map_crc":598803192,"workshop_id":779296053,"map_name":"de_blackgold"},{"map_crc":4019078009,"workshop_id":779296576,"map_name":"de_castle"},{"map_crc":1662453271,"workshop_id":779297203,"map_name":"de_mist"},{"map_crc":1759815840,"workshop_id":779297608,"map_name":"de_overgrown"},{"map_crc":1599338761,"workshop_id":779297910,"map_name":"de_seaside"},{"map_crc":1671995046,"workshop_id":779298293,"map_name":"de_ali"},{"map_crc":684414038,"workshop_id":779298590,"map_name":"de_favela"},{"map_crc":3048513188,"workshop_id":779276562,"map_name":"de_coast"},{"map_crc":3714287174,"workshop_id":779277130,"map_name":"de_empire"},{"map_crc":4047972089,"workshop_id":779277728,"map_name":"de_mikla"},{"map_crc":2737543307,"workshop_id":779278303,"map_name":"de_royal"},{"map_crc":462819071,"workshop_id":779279081,"map_name":"de_santorini"},{"map_crc":3494074619,"workshop_id":779279760,"map_name":"de_tulip"},{"map_crc":1971998467,"workshop_id":779280547,"map_name":"de_log"},{"map_crc":39849554,"workshop_id":779281044,"map_name":"de_rails"},{"map_crc":785340739,"workshop_id":779281816,"map_name":"de_resort"},{"map_crc":3725374664,"workshop_id":779282483,"map_name":"de_season"},{"map_crc":965633239,"workshop_id":779282997,"map_name":"de_zoo"},{"map_crc":1372363045,"workshop_id":786667121,"map_name":"de_inferno"},{"map_crc":2444520841,"workshop_id":786667836,"map_name":"de_cache"},{"map_crc":3406488821,"workshop_id":801412391,"map_name":"de_dust2"},{"map_crc":4074319541,"workshop_id":801412877,"map_name":"de_train"},{"map_crc":2437276733,"workshop_id":814076957,"map_name":"de_nuke"},{"map_crc":149708965,"workshop_id":822900618,"map_name":"de_nuke"},{"map_crc":1953099299,"workshop_id":841001919,"map_name":"de_mirage"},{"map_crc":1903442639,"workshop_id":841002490,"map_name":"de_cache"},{"map_crc":2121788827,"workshop_id":841002794,"map_name":"de_inferno"},{"map_crc":787242208,"workshop_id":855577410,"map_name":"de_cbble"},{"map_crc":1954603074,"workshop_id":855578238,"map_name":"de_dust2"},{"map_crc":487988445,"workshop_id":855578744,"map_name":"de_train"},{"map_crc":2285880676,"workshop_id":855579276,"map_name":"de_overpass"},{"map_crc":2540839779,"workshop_id":864122863,"map_name":"de_overpass"},{"map_crc":1607849851,"workshop_id":864122190,"map_name":"de_cbble"},{"map_crc":198936178,"workshop_id":864124450,"map_name":"de_nuke"},{"map_crc":3259980128,"workshop_id":864123709,"map_name":"de_cache"},{"map_crc":988540422,"workshop_id":884195732,"map_name":"de_cbble"},{"map_crc":2498324102,"workshop_id":884196682,"map_name":"cs_italy"},{"map_crc":1060854352,"workshop_id":884197898,"map_name":"de_cache"},{"map_crc":679560331,"workshop_id":884198821,"map_name":"de_nuke"},{"map_crc":2749586970,"workshop_id":884953651,"map_name":"de_canals"},{"map_crc":1085448152,"workshop_id":885367711,"map_name":"de_nuke"},{"map_crc":965922107,"workshop_id":885368482,"map_name":"cs_assault"},{"map_crc":4204300045,"workshop_id":887994799,"map_name":"de_cache"},{"map_crc":972836048,"workshop_id":887995693,"map_name":"de_canals"},{"map_crc":2180831103,"workshop_id":889090614,"map_name":"de_canals"},{"map_crc":2186378655,"workshop_id":892938411,"map_name":"de_canals"},{"map_crc":2023209806,"workshop_id":894174463,"map_name":"cs_militia"},{"map_crc":2026378635,"workshop_id":908434318,"map_name":"de_canals"},{"map_crc":2718369684,"workshop_id":909675543,"map_name":"de_cbble"},{"map_crc":2022765492,"workshop_id":909675955,"map_name":"cs_militia"},{"map_crc":753379332,"workshop_id":935704446,"map_name":"cs_insertion"},{"map_crc":38684184,"workshop_id":936368091,"map_name":"de_blackgold"},{"map_crc":883960581,"workshop_id":937719496,"map_name":"de_shipped"},{"map_crc":3469664392,"workshop_id":940642780,"map_name":"cs_insertion"},{"map_crc":1966908865,"workshop_id":948347920,"map_name":"de_inferno"},{"map_crc":754870349,"workshop_id":948353346,"map_name":"de_cbble"},{"map_crc":1560576749,"workshop_id":951327114,"map_name":"de_cache"},{"map_crc":1151705951,"workshop_id":1097447025,"map_name":"de_overpass"},{"map_crc":1730240413,"workshop_id":1097448569,"map_name":"de_inferno"},{"map_crc":1923960555,"workshop_id":1164734849,"map_name":"de_dust2"},{"map_crc":3514045712,"workshop_id":1175332019,"map_name":"de_dust2"},{"map_crc":3586949499,"workshop_id":1179952448,"map_name":"de_dust2"},{"map_crc":3899912930,"workshop_id":1182713175,"map_name":"de_dust2"},{"map_crc":3834002217,"workshop_id":1193847586,"map_name":"de_thrill"},{"map_crc":2388516656,"workshop_id":1193853308,"map_name":"de_lite"},{"map_crc":4176674156,"workshop_id":1193860306,"map_name":"de_blackgold"},{"map_crc":3305147053,"workshop_id":1193865998,"map_name":"cs_militia"},{"map_crc":346516097,"workshop_id":1193870504,"map_name":"de_vertigo"},{"map_crc":3258553541,"workshop_id":1193875520,"map_name":"de_aztec"},{"map_crc":1112256951,"workshop_id":1193879022,"map_name":"de_dust"},{"map_crc":4225130765,"workshop_id":1194814515,"map_name":"de_dust2"},{"map_crc":2834104219,"workshop_id":1196984847,"map_name":"de_train"},{"map_crc":328297621,"workshop_id":1257701053,"map_name":"de_cbble"},{"map_crc":1611470151,"workshop_id":1257704998,"map_name":"de_overpass"},{"map_crc":745019601,"workshop_id":1272679952,"map_name":"de_cache"},{"map_crc":2714661365,"workshop_id":1293553700,"map_name":"de_shipped"},{"map_crc":1349455507,"workshop_id":1293604953,"map_name":"de_canals"},{"map_crc":1429090831,"workshop_id":1294520739,"map_name":"de_nuke"},{"map_crc":2848812001,"workshop_id":1301695658,"map_name":"de_nuke"},{"map_crc":2400579432,"workshop_id":1309054267,"map_name":"de_canals"},{"map_crc":2105703744,"workshop_id":1337290144,"map_name":"de_canals"},{"map_crc":4246019248,"workshop_id":1345858192,"map_name":"de_dust2"},{"map_crc":694707911,"workshop_id":1345859676,"map_name":"de_nuke"},{"map_crc":364649140,"workshop_id":1374615195,"map_name":"de_shortnuke"},{"map_crc":3620220936,"workshop_id":1386181443,"map_name":"de_nuke"},{"map_crc":3222627201,"workshop_id":1386182062,"map_name":"de_overpass"},{"map_crc":1761400225,"workshop_id":1400380125,"map_name":"de_canals"},{"map_crc":2683197182,"workshop_id":1409243131,"map_name":"de_mirage"},{"map_crc":1711067458,"workshop_id":1433404064,"map_name":"de_mirage"},{"map_crc":3435941461,"workshop_id":1433412429,"map_name":"de_overpass"},{"map_crc":3399090610,"workshop_id":1433417529,"map_name":"de_cache"},{"map_crc":3480756671,"workshop_id":1489345536,"map_name":"de_mirage"},{"map_crc":55784088,"workshop_id":1489346415,"map_name":"de_train"},{"map_crc":1799531009,"workshop_id":1528061920,"map_name":"de_nuke"},{"map_crc":24432047,"workshop_id":1528062576,"map_name":"cs_office"},{"map_crc":3592367259,"workshop_id":1534022416,"map_name":"de_canals"},{"map_crc":1443022770,"workshop_id":1542127528,"map_name":"de_cbble"},{"map_crc":2734658340,"workshop_id":1542138678,"map_name":"de_subzero"},{"map_crc":1929955606,"workshop_id":1542152373,"map_name":"de_biome"},{"map_crc":1422585851,"workshop_id":1557755905,"map_name":"de_cbble"},{"map_crc":2238852608,"workshop_id":1565094975,"map_name":"de_subzero"},{"map_crc":2746601304,"workshop_id":1576299850,"map_name":"de_biome"},{"map_crc":19123987,"workshop_id":1576301161,"map_name":"de_subzero"},{"map_crc":3067937333,"workshop_id":1633338669,"map_name":"de_biome"},{"map_crc":624708579,"workshop_id":1633463725,"map_name":"de_subzero"},{"map_crc":2171383539,"workshop_id":1633464418,"map_name":"de_austria"},{"map_crc":1868397234,"workshop_id":1640972706,"map_name":"de_vertigo"},{"map_crc":1055793844,"workshop_id":1666769437,"map_name":"de_abbey"},{"map_crc":1779435148,"workshop_id":1666770780,"map_name":"de_biome"},{"map_crc":3847680298,"workshop_id":1666772172,"map_name":"de_zoo"},{"map_crc":784967567,"workshop_id":1675727752,"map_name":"de_abbey"},{"map_crc":4165710957,"workshop_id":1675728803,"map_name":"de_biome"},{"map_crc":1594538885,"workshop_id":1675729784,"map_name":"de_vertigo"},{"map_crc":3592367259,"workshop_id":1680376485,"map_name":"de_canals"},{"map_crc":2627881398,"workshop_id":1681197481,"map_name":"de_vertigo"},{"map_crc":2216800179,"workshop_id":1687698553,"map_name":"de_vertigo"},{"map_crc":4227116290,"workshop_id":1689422006,"map_name":"de_vertigo"},{"map_crc":851794800,"workshop_id":1689423610,"map_name":"de_abbey"},{"map_crc":2232065438,"workshop_id":1696771696,"map_name":"de_vertigo"},{"map_crc":4089328595,"workshop_id":1701546035,"map_name":"de_vertigo"},{"map_crc":2199324930,"workshop_id":1701547277,"map_name":"de_nuke"},{"map_crc":4239346301,"workshop_id":1701556709,"map_name":"de_abbey"},{"map_crc":940309033,"workshop_id":1722473629,"map_name":"de_vertigo"},{"map_crc":1201002146,"workshop_id":1722477085,"map_name":"de_train"},{"map_crc":3172944958,"workshop_id":1722482127,"map_name":"de_overpass"},{"map_crc":3345967379,"workshop_id":1722485033,"map_name":"de_inferno"},{"map_crc":167451395,"workshop_id":1722487623,"map_name":"de_cbble"},{"map_crc":875334740,"workshop_id":1723162652,"map_name":"de_biome"},{"map_crc":3888414482,"workshop_id":1723172623,"map_name":"de_abbey"},{"map_crc":3594649813,"workshop_id":1729358770,"map_name":"de_vertigo"},{"map_crc":553181964,"workshop_id":1729357842,"map_name":"de_mirage"},{"map_crc":1589080237,"workshop_id":1735137147,"map_name":"de_train"},{"map_crc":3082674078,"workshop_id":1736558884,"map_name":"cs_workout"},{"map_crc":2018119748,"workshop_id":1736561513,"map_name":"de_ruby"},{"map_crc":3727324750,"workshop_id":1748160237,"map_name":"de_vertigo"},{"map_crc":1897133176,"workshop_id":1755227189,"map_name":"de_ruby"},{"map_crc":1357023846,"workshop_id":1773333823,"map_name":"de_dust2"},{"map_crc":3379158839,"workshop_id":1775103773,"map_name":"de_dust2"},{"map_crc":3294371934,"workshop_id":1801844662,"map_name":"cs_workout"},{"map_crc":3902730729,"workshop_id":1801847317,"map_name":"de_ruby"},{"map_crc":691658072,"workshop_id":1801849442,"map_name":"de_dust2"},{"map_crc":1763244944,"workshop_id":1801851338,"map_name":"de_vertigo"},{"map_crc":582058337,"workshop_id":1821117172,"map_name":"de_overpass"},{"map_crc":85780062,"workshop_id":1821365096,"map_name":"cs_workout"},{"map_crc":2221485447,"workshop_id":1823168254,"map_name":"de_seaside"},{"map_crc":3414630357,"workshop_id":1827983442,"map_name":"de_breach"},{"map_crc":1845739875,"workshop_id":1833745895,"map_name":"de_breach"},{"map_crc":1649926179,"workshop_id":1839869047,"map_name":"de_nuke"},{"map_crc":1406182878,"workshop_id":1839890927,"map_name":"de_overpass"},{"map_crc":1613567573,"workshop_id":1878850362,"map_name":"de_ruby"},{"map_crc":3637804359,"workshop_id":1885079840,"map_name":"de_vertigo"},{"map_crc":2043118189,"workshop_id":1885082371,"map_name":"de_dust2"},{"map_crc":43072076,"workshop_id":1885085042,"map_name":"de_nuke"},{"map_crc":1901448379,"workshop_id":1891729882,"map_name":"de_cache"},{"map_crc":847801804,"workshop_id":1903458155,"map_name":"de_cache"},{"map_crc":2963342968,"workshop_id":1903460768,"map_name":"de_breach"},{"map_crc":472333779,"workshop_id":1912343309,"map_name":"de_zoo"},{"map_crc":3473794386,"workshop_id":1912344800,"map_name":"de_seaside"},{"map_crc":4104015631,"workshop_id":1912915853,"map_name":"de_nuke"},{"map_crc":3266570417,"workshop_id":1912918053,"map_name":"de_overpass"},{"map_crc":440099467,"workshop_id":1913686841,"map_name":"de_mirage"},{"map_crc":2867562853,"workshop_id":1913688091,"map_name":"de_vertigo"},{"map_crc":1187761181,"workshop_id":1918539045,"map_name":"de_overpass"},{"map_crc":4027721086,"workshop_id":1918541496,"map_name":"de_inferno"},{"map_crc":1169844696,"workshop_id":1918544705,"map_name":"de_nuke"},{"map_crc":3801989810,"workshop_id":1921543573,"map_name":"de_cache"},{"map_crc":2001533627,"workshop_id":1921559276,"map_name":"de_studio"},{"map_crc":4267242543,"workshop_id":1935964674,"map_name":"de_overpass"},{"map_crc":649906979,"workshop_id":1935967249,"map_name":"de_mirage"},{"map_crc":4175765460,"workshop_id":1968426367,"map_name":"de_studio"},{"map_crc":2284626039,"workshop_id":1969946145,"map_name":"de_studio"},{"map_crc":1976530970,"workshop_id":1981380430,"map_name":"de_cache"},{"map_crc":702125823,"workshop_id":1981398553,"map_name":"de_dust2"},{"map_crc":446677603,"workshop_id":1981405908,"map_name":"de_mirage"},{"map_crc":2742246669,"workshop_id":1981409042,"map_name":"de_train"},{"map_crc":4288048149,"workshop_id":2006614724,"map_name":"de_studio"},{"map_crc":67423154,"workshop_id":2006611084,"map_name":"de_breach"},{"map_crc":2341807164,"workshop_id":2012392887,"map_name":"de_vertigo"},{"map_crc":229927523,"workshop_id":2036357456,"map_name":"de_breach"},{"map_crc":2713336551,"workshop_id":2036368546,"map_name":"de_studio"},{"map_crc":1750839140,"workshop_id":2054444687,"map_name":"de_anubis"},{"map_crc":1711475311,"workshop_id":2054449784,"map_name":"de_chlorine"},{"map_crc":2910759191,"workshop_id":2054468706,"map_name":"de_inferno"},{"map_crc":1072830023,"workshop_id":2054480952,"map_name":"cs_office"},{"map_crc":2986371766,"workshop_id":2054485840,"map_name":"de_overpass"},{"map_crc":585298464,"workshop_id":2054490519,"map_name":"de_mirage"},{"map_crc":1971888498,"workshop_id":2054499315,"map_name":"de_train"},{"map_crc":3636113230,"workshop_id":2054503589,"map_name":"de_dust2"},{"map_crc":2610818927,"workshop_id":2054507930,"map_name":"de_vertigo"},{"map_crc":2395751182,"workshop_id":2063389416,"map_name":"de_anubis"},{"map_crc":3070594518,"workshop_id":2063395311,"map_name":"de_chlorine"},{"map_crc":1222987282,"workshop_id":2071551442,"map_name":"de_vertigo"},{"map_crc":1621419284,"workshop_id":2071570799,"map_name":"de_overpass"},{"map_crc":1143470049,"workshop_id":2071576947,"map_name":"de_cache"},{"map_crc":499030300,"workshop_id":2071588971,"map_name":"de_shortnuke"},{"map_crc":3383555137,"workshop_id":2079511962,"map_name":"de_inferno"},{"map_crc":3543588766,"workshop_id":2079518314,"map_name":"de_chlorine"},{"map_crc":1967282,"workshop_id":2118102682,"map_name":"de_anubis"},{"map_crc":2860016395,"workshop_id":2118107743,"map_name":"de_chlorine"},{"map_crc":1835897799,"workshop_id":2126164737,"map_name":"de_inferno"},{"map_crc":3636113230,"workshop_id":2126168252,"map_name":"de_dust2"},{"map_crc":449560063,"workshop_id":2126169449,"map_name":"de_mirage"},{"map_crc":2641294235,"workshop_id":2126170771,"map_name":"de_overpass"},{"map_crc":1759423733,"workshop_id":2126172171,"map_name":"de_train"},{"map_crc":375057944,"workshop_id":2126260848,"map_name":"de_cache"},{"map_crc":2182763674,"workshop_id":2131548624,"map_name":"de_nuke"},{"map_crc":902808405,"workshop_id":2131550446,"map_name":"de_shortnuke"},{"map_crc":447769027,"workshop_id":2175452749,"map_name":"de_chlorine"},{"map_crc":1678532468,"workshop_id":2179783089,"map_name":"de_swamp"},{"map_crc":757494310,"workshop_id":2179787687,"map_name":"de_mutiny"},{"map_crc":1109128738,"workshop_id":2181729057,"map_name":"de_mutiny"},{"map_crc":2251975140,"workshop_id":2187524096,"map_name":"de_anubis"},{"map_crc":1207857107,"workshop_id":2187535474,"map_name":"de_swamp"},{"map_crc":2033573190,"workshop_id":2187542475,"map_name":"de_mutiny"},{"map_crc":2723481500,"workshop_id":2210204218,"map_name":"de_mutiny"},{"map_crc":904657061,"workshop_id":2210219776,"map_name":"de_swamp"},{"map_crc":441329610,"workshop_id":2210233147,"map_name":"de_cache"},{"map_crc":3475973139,"workshop_id":2223402056,"map_name":"de_mutiny"},{"map_crc":3423164190,"workshop_id":2223406006,"map_name":"de_anubis"},{"map_crc":1936772555,"workshop_id":2307643103,"map_name":"de_mirage"},{"map_crc":3398892690,"workshop_id":2307653485,"map_name":"de_inferno"},{"map_crc":157508062,"workshop_id":2307656161,"map_name":"de_overpass"},{"map_crc":3160854265,"workshop_id":2307659777,"map_name":"de_nuke"},{"map_crc":1631289957,"workshop_id":2307662386,"map_name":"de_train"},{"map_crc":611946841,"workshop_id":2307665068,"map_name":"de_vertigo"},{"map_crc":282161188,"workshop_id":2307669771,"map_name":"de_dust2"},{"map_crc":1183710180,"workshop_id":2307695435,"map_name":"de_swamp"},{"map_crc":2918195333,"workshop_id":2307680181,"map_name":"de_mutiny"},{"map_crc":1246179162,"workshop_id":2307675806,"map_name":"gd_rialto"},{"map_crc":1914640907,"workshop_id":2307685877,"map_name":"de_cache"},{"map_crc":2147058902,"workshop_id":2312272701,"map_name":"de_guard"},{"map_crc":2269574842,"workshop_id":2314436317,"map_name":"de_ancient"},{"map_crc":1677613133,"workshop_id":2314440887,"map_name":"de_mirage"},{"map_crc":1492171397,"workshop_id":2317088027,"map_name":"de_elysion"},{"map_crc":3901232464,"workshop_id":2323254747,"map_name":"de_anubis"},{"map_crc":2807833708,"workshop_id":2323261514,"map_name":"cs_apollo"},{"map_crc":3116262833,"workshop_id":2324362992,"map_name":"de_inferno"},{"map_crc":2568523904,"workshop_id":2324369082,"map_name":"de_mirage"},{"map_crc":2649678568,"workshop_id":2324376136,"map_name":"de_nuke"},{"map_crc":2452313975,"workshop_id":2324383948,"map_name":"de_train"},{"map_crc":2868626369,"workshop_id":2324387137,"map_name":"de_vertigo"},{"map_crc":1090805620,"workshop_id":2351286188,"map_name":"de_mirage"},{"map_crc":4033654929,"workshop_id":2351289954,"map_name":"de_train"},{"map_crc":2156109504,"workshop_id":2351298317,"map_name":"de_ancient"},{"map_crc":3871507659,"workshop_id":2351311256,"map_name":"de_guard"},{"map_crc":874803390,"workshop_id":2351316052,"map_name":"de_engage"},{"map_crc":550879784,"workshop_id":2352519394,"map_name":"de_cache"},{"map_crc":3829620128,"workshop_id":2368357235,"map_name":"cs_apollo"},{"map_crc":1456019192,"workshop_id":2375944707,"map_name":"de_elysion"},{"map_crc":392360228,"workshop_id":2392017888,"map_name":"de_engage"},{"map_crc":3229223836,"workshop_id":2392024743,"map_name":"cs_apollo"},{"map_crc":1833504216,"workshop_id":2399153466,"map_name":"de_anubis"},{"map_crc":319518790,"workshop_id":2425904910,"map_name":"de_guard"},{"map_crc":947497149,"workshop_id":2472509570,"map_name":"de_ancient"},{"map_crc":2229566652,"workshop_id":2472514582,"map_name":"de_anubis"},{"map_crc":2566589949,"workshop_id":2472521264,"map_name":"cs_apollo"},{"map_crc":1435013528,"workshop_id":2472526342,"map_name":"de_nuke"},{"map_crc":818606442,"workshop_id":2472529701,"map_name":"de_guard"},{"map_crc":151200708,"workshop_id":2472534059,"map_name":"de_elysion"},{"map_crc":1317607445,"workshop_id":2472538499,"map_name":"de_engage"},{"map_crc":3026502447,"workshop_id":2477026536,"map_name":"de_pitstop"},{"map_crc":2987912495,"workshop_id":2485232034,"map_name":"de_grind"},{"map_crc":442889588,"workshop_id":2485236196,"map_name":"de_ancient"},{"map_crc":3900295690,"workshop_id":2497709776,"map_name":"de_grind"},{"map_crc":2747448729,"workshop_id":2497718914,"map_name":"de_mocha"},{"map_crc":2895852907,"workshop_id":2497723828,"map_name":"de_cache"},{"map_crc":3460594272,"workshop_id":2497843855,"map_name":"de_ancient"},{"map_crc":3761898658,"workshop_id":2511449026,"map_name":"de_pitstop"},{"map_crc":4163046011,"workshop_id":2552790673,"map_name":"de_pitstop"},{"map_crc":64907393,"workshop_id":2552798564,"map_name":"de_mocha"},{"map_crc":4242573864,"workshop_id":2553791086,"map_name":"de_calavera"},{"map_crc":2940774496,"workshop_id":2606407435,"map_name":"de_cache"},{"map_crc":2202091687,"workshop_id":2606416005,"map_name":"de_mocha"},{"map_crc":1663935558,"workshop_id":2606421220,"map_name":"de_grind"},{"map_crc":2874582833,"workshop_id":2606431684,"map_name":"de_ancient"},{"map_crc":1654113222,"workshop_id":2606435621,"map_name":"de_dust2"},{"map_crc":4271092677,"workshop_id":2606439175,"map_name":"de_inferno"},{"map_crc":3171597260,"workshop_id":2606443744,"map_name":"de_pitstop"},{"map_crc":3085465808,"workshop_id":472138951,"map_name":"de_calavera"}]}